@backstage/plugin-techdocs 0.12.14 → 0.13.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,137 @@
1
1
  # @backstage/plugin-techdocs
2
2
 
3
+ ## 0.13.2-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 359c31e31d: Added support for documentation using the raw `<source>` tag to point to relative resources like audio or video files.
8
+ - Updated dependencies
9
+ - @backstage/core-components@0.8.7-next.0
10
+ - @backstage/integration-react@0.1.20-next.0
11
+ - @backstage/plugin-catalog@0.7.11-next.0
12
+ - @backstage/plugin-catalog-react@0.6.13-next.0
13
+ - @backstage/plugin-search@0.6.1-next.0
14
+
15
+ ## 0.13.1
16
+
17
+ ### Patch Changes
18
+
19
+ - bdc53553eb: chore(deps): bump `react-text-truncate` from 0.16.0 to 0.17.0
20
+ - a64f99f734: Code snippets now include a "copy to clipboard" button.
21
+ - Updated dependencies
22
+ - @backstage/core-components@0.8.6
23
+ - @backstage/plugin-search@0.6.0
24
+ - @backstage/plugin-catalog@0.7.10
25
+
26
+ ## 0.13.0
27
+
28
+ ### Minor Changes
29
+
30
+ - aecfe4f403: Make `TechDocsClient` and `TechDocsStorageClient` use the `FetchApi`. You now
31
+ need to pass in an instance of that API when constructing the client, if you
32
+ create a custom instance in your app.
33
+
34
+ If you are replacing the factory:
35
+
36
+ ```diff
37
+ +import { fetchApiRef } from '@backstage/core-plugin-api';
38
+
39
+ createApiFactory({
40
+ api: techdocsStorageApiRef,
41
+ deps: {
42
+ configApi: configApiRef,
43
+ discoveryApi: discoveryApiRef,
44
+ identityApi: identityApiRef,
45
+ + fetchApi: fetchApiRef,
46
+ },
47
+ factory: ({
48
+ configApi,
49
+ discoveryApi,
50
+ identityApi,
51
+ + fetchApi,
52
+ }) =>
53
+ new TechDocsStorageClient({
54
+ configApi,
55
+ discoveryApi,
56
+ identityApi,
57
+ + fetchApi,
58
+ }),
59
+ }),
60
+ createApiFactory({
61
+ api: techdocsApiRef,
62
+ deps: {
63
+ configApi: configApiRef,
64
+ discoveryApi: discoveryApiRef,
65
+ - identityApi: identityApiRef,
66
+ + fetchApi: fetchApiRef,
67
+ },
68
+ factory: ({
69
+ configApi,
70
+ discoveryApi,
71
+ - identityApi,
72
+ + fetchApi,
73
+ }) =>
74
+ new TechDocsClient({
75
+ configApi,
76
+ discoveryApi,
77
+ - identityApi,
78
+ + fetchApi,
79
+ }),
80
+ }),
81
+ ```
82
+
83
+ If instantiating directly:
84
+
85
+ ```diff
86
+ +import { fetchApiRef } from '@backstage/core-plugin-api';
87
+
88
+ +const fetchApi = useApi(fetchApiRef);
89
+ const storageClient = new TechDocsStorageClient({
90
+ configApi,
91
+ discoveryApi,
92
+ identityApi,
93
+ + fetchApi,
94
+ });
95
+ const techdocsClient = new TechDocsClient({
96
+ configApi,
97
+ discoveryApi,
98
+ - identityApi,
99
+ + fetchApi,
100
+ }),
101
+ ```
102
+
103
+ ### Patch Changes
104
+
105
+ - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
106
+ - 29710c91c2: use lighter color for block quotes and horizontal rulers
107
+ - Updated dependencies
108
+ - @backstage/core-components@0.8.5
109
+ - @backstage/integration@0.7.2
110
+ - @backstage/plugin-search@0.5.6
111
+ - @backstage/core-plugin-api@0.6.0
112
+ - @backstage/plugin-catalog@0.7.9
113
+ - @backstage/plugin-catalog-react@0.6.12
114
+ - @backstage/config@0.1.13
115
+ - @backstage/catalog-model@0.9.10
116
+ - @backstage/integration-react@0.1.19
117
+
118
+ ## 0.12.15-next.0
119
+
120
+ ### Patch Changes
121
+
122
+ - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
123
+ - 29710c91c2: use lighter color for block quotes and horizontal rulers
124
+ - Updated dependencies
125
+ - @backstage/core-components@0.8.5-next.0
126
+ - @backstage/core-plugin-api@0.6.0-next.0
127
+ - @backstage/plugin-catalog@0.7.9-next.0
128
+ - @backstage/config@0.1.13-next.0
129
+ - @backstage/plugin-catalog-react@0.6.12-next.0
130
+ - @backstage/plugin-search@0.5.6-next.0
131
+ - @backstage/catalog-model@0.9.10-next.0
132
+ - @backstage/integration-react@0.1.19-next.0
133
+ - @backstage/integration@0.7.2-next.0
134
+
3
135
  ## 0.12.14
4
136
 
5
137
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
3
- import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
3
+ import { DiscoveryApi, FetchApi, IdentityApi } from '@backstage/core-plugin-api';
4
4
  import * as _backstage_catalog_model from '@backstage/catalog-model';
5
5
  import { Entity, LocationSpec, EntityName } from '@backstage/catalog-model';
6
6
  import { Config } from '@backstage/config';
@@ -19,10 +19,33 @@ declare type TechDocsEntityMetadata = Entity & {
19
19
  locationMetadata?: LocationSpec;
20
20
  };
21
21
 
22
+ /**
23
+ * Utility API reference for the {@link TechDocsStorageApi}.
24
+ *
25
+ * @public
26
+ */
22
27
  declare const techdocsStorageApiRef: _backstage_core_plugin_api.ApiRef<TechDocsStorageApi>;
28
+ /**
29
+ * Utility API reference for the {@link TechDocsApi}.
30
+ *
31
+ * @public
32
+ */
23
33
  declare const techdocsApiRef: _backstage_core_plugin_api.ApiRef<TechDocsApi>;
34
+ /**
35
+ * The outcome of a docs sync operation.
36
+ *
37
+ * @public
38
+ */
24
39
  declare type SyncResult = 'cached' | 'updated';
40
+ /**
41
+ * API which talks to TechDocs storage to fetch files to render.
42
+ *
43
+ * @public
44
+ */
25
45
  interface TechDocsStorageApi {
46
+ /**
47
+ * Set to techdocs.requestUrl as the URL for techdocs-backend API.
48
+ */
26
49
  getApiOrigin(): Promise<string>;
27
50
  getStorageUrl(): Promise<string>;
28
51
  getBuilder(): Promise<string>;
@@ -30,7 +53,15 @@ interface TechDocsStorageApi {
30
53
  syncEntityDocs(entityId: EntityName, logHandler?: (line: string) => void): Promise<SyncResult>;
31
54
  getBaseUrl(oldBaseUrl: string, entityId: EntityName, path: string): Promise<string>;
32
55
  }
56
+ /**
57
+ * API to talk to techdocs-backend.
58
+ *
59
+ * @public
60
+ */
33
61
  interface TechDocsApi {
62
+ /**
63
+ * Set to techdocs.requestUrl as the URL for techdocs-backend API.
64
+ */
34
65
  getApiOrigin(): Promise<string>;
35
66
  getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata>;
36
67
  getEntityMetadata(entityId: EntityName): Promise<TechDocsEntityMetadata>;
@@ -38,15 +69,17 @@ interface TechDocsApi {
38
69
 
39
70
  /**
40
71
  * API to talk to `techdocs-backend`.
72
+ *
73
+ * @public
41
74
  */
42
75
  declare class TechDocsClient implements TechDocsApi {
43
76
  configApi: Config;
44
77
  discoveryApi: DiscoveryApi;
45
- identityApi: IdentityApi;
46
- constructor({ configApi, discoveryApi, identityApi, }: {
78
+ private fetchApi;
79
+ constructor(options: {
47
80
  configApi: Config;
48
81
  discoveryApi: DiscoveryApi;
49
- identityApi: IdentityApi;
82
+ fetchApi: FetchApi;
50
83
  });
51
84
  getApiOrigin(): Promise<string>;
52
85
  /**
@@ -71,15 +104,19 @@ declare class TechDocsClient implements TechDocsApi {
71
104
  }
72
105
  /**
73
106
  * API which talks to TechDocs storage to fetch files to render.
107
+ *
108
+ * @public
74
109
  */
75
110
  declare class TechDocsStorageClient implements TechDocsStorageApi {
76
111
  configApi: Config;
77
112
  discoveryApi: DiscoveryApi;
78
113
  identityApi: IdentityApi;
79
- constructor({ configApi, discoveryApi, identityApi, }: {
114
+ private fetchApi;
115
+ constructor(options: {
80
116
  configApi: Config;
81
117
  discoveryApi: DiscoveryApi;
82
118
  identityApi: IdentityApi;
119
+ fetchApi: FetchApi;
83
120
  });
84
121
  getApiOrigin(): Promise<string>;
85
122
  getStorageUrl(): Promise<string>;
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createApiRef, createRouteRef, useRouteRef, useApi, configApiRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
1
+ import { createApiRef, createRouteRef, useRouteRef, useApi, configApiRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, fetchApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
2
2
  import { ResponseError, NotFoundError } from '@backstage/errors';
3
3
  import { EventSourcePolyfill } from 'event-source-polyfill';
4
4
  import React, { useEffect, useState, useReducer, useRef, useMemo, createContext, useContext, useCallback } from 'react';
@@ -37,14 +37,10 @@ const techdocsApiRef = createApiRef({
37
37
  });
38
38
 
39
39
  class TechDocsClient {
40
- constructor({
41
- configApi,
42
- discoveryApi,
43
- identityApi
44
- }) {
45
- this.configApi = configApi;
46
- this.discoveryApi = discoveryApi;
47
- this.identityApi = identityApi;
40
+ constructor(options) {
41
+ this.configApi = options.configApi;
42
+ this.discoveryApi = options.discoveryApi;
43
+ this.fetchApi = options.fetchApi;
48
44
  }
49
45
  async getApiOrigin() {
50
46
  var _a;
@@ -54,10 +50,7 @@ class TechDocsClient {
54
50
  const { kind, namespace, name } = entityId;
55
51
  const apiOrigin = await this.getApiOrigin();
56
52
  const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;
57
- const token = await this.identityApi.getIdToken();
58
- const request = await fetch(`${requestUrl}`, {
59
- headers: token ? { Authorization: `Bearer ${token}` } : {}
60
- });
53
+ const request = await this.fetchApi.fetch(`${requestUrl}`);
61
54
  if (!request.ok) {
62
55
  throw await ResponseError.fromResponse(request);
63
56
  }
@@ -67,10 +60,7 @@ class TechDocsClient {
67
60
  const { kind, namespace, name } = entityId;
68
61
  const apiOrigin = await this.getApiOrigin();
69
62
  const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;
70
- const token = await this.identityApi.getIdToken();
71
- const request = await fetch(`${requestUrl}`, {
72
- headers: token ? { Authorization: `Bearer ${token}` } : {}
73
- });
63
+ const request = await this.fetchApi.fetch(`${requestUrl}`);
74
64
  if (!request.ok) {
75
65
  throw await ResponseError.fromResponse(request);
76
66
  }
@@ -78,14 +68,11 @@ class TechDocsClient {
78
68
  }
79
69
  }
80
70
  class TechDocsStorageClient {
81
- constructor({
82
- configApi,
83
- discoveryApi,
84
- identityApi
85
- }) {
86
- this.configApi = configApi;
87
- this.discoveryApi = discoveryApi;
88
- this.identityApi = identityApi;
71
+ constructor(options) {
72
+ this.configApi = options.configApi;
73
+ this.discoveryApi = options.discoveryApi;
74
+ this.identityApi = options.identityApi;
75
+ this.fetchApi = options.fetchApi;
89
76
  }
90
77
  async getApiOrigin() {
91
78
  var _a;
@@ -102,10 +89,7 @@ class TechDocsStorageClient {
102
89
  const { kind, namespace, name } = entityId;
103
90
  const storageUrl = await this.getStorageUrl();
104
91
  const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;
105
- const token = await this.identityApi.getIdToken();
106
- const request = await fetch(`${url.endsWith("/") ? url : `${url}/`}index.html`, {
107
- headers: token ? { Authorization: `Bearer ${token}` } : {}
108
- });
92
+ const request = await this.fetchApi.fetch(`${url.endsWith("/") ? url : `${url}/`}index.html`);
109
93
  let errorMessage = "";
110
94
  switch (request.status) {
111
95
  case 404:
@@ -125,7 +109,7 @@ class TechDocsStorageClient {
125
109
  const { kind, namespace, name } = entityId;
126
110
  const apiOrigin = await this.getApiOrigin();
127
111
  const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;
128
- const token = await this.identityApi.getIdToken();
112
+ const { token } = await this.identityApi.getCredentials();
129
113
  return new Promise((resolve, reject) => {
130
114
  const source = new EventSourcePolyfill(url, {
131
115
  withCredentials: true,
@@ -495,12 +479,14 @@ const techdocsPlugin = createPlugin({
495
479
  deps: {
496
480
  configApi: configApiRef,
497
481
  discoveryApi: discoveryApiRef,
498
- identityApi: identityApiRef
482
+ identityApi: identityApiRef,
483
+ fetchApi: fetchApiRef
499
484
  },
500
- factory: ({ configApi, discoveryApi, identityApi }) => new TechDocsStorageClient({
485
+ factory: ({ configApi, discoveryApi, identityApi, fetchApi }) => new TechDocsStorageClient({
501
486
  configApi,
502
487
  discoveryApi,
503
- identityApi
488
+ identityApi,
489
+ fetchApi
504
490
  })
505
491
  }),
506
492
  createApiFactory({
@@ -508,12 +494,12 @@ const techdocsPlugin = createPlugin({
508
494
  deps: {
509
495
  configApi: configApiRef,
510
496
  discoveryApi: discoveryApiRef,
511
- identityApi: identityApiRef
497
+ fetchApi: fetchApiRef
512
498
  },
513
- factory: ({ configApi, discoveryApi, identityApi }) => new TechDocsClient({
499
+ factory: ({ configApi, discoveryApi, fetchApi }) => new TechDocsClient({
514
500
  configApi,
515
501
  discoveryApi,
516
- identityApi
502
+ fetchApi
517
503
  })
518
504
  })
519
505
  ],
@@ -598,6 +584,7 @@ const addBaseUrl = ({
598
584
  await Promise.all([
599
585
  updateDom(dom.querySelectorAll("img"), "src"),
600
586
  updateDom(dom.querySelectorAll("script"), "src"),
587
+ updateDom(dom.querySelectorAll("source"), "src"),
601
588
  updateDom(dom.querySelectorAll("link"), "href"),
602
589
  updateDom(dom.querySelectorAll("a[download]"), "href")
603
590
  ]);
@@ -696,6 +683,22 @@ const addLinkClickListener = ({
696
683
  };
697
684
  };
698
685
 
686
+ const copyToClipboard = () => {
687
+ return (dom) => {
688
+ Array.from(dom.querySelectorAll("code")).forEach((codeElem) => {
689
+ var _a;
690
+ const button = document.createElement("button");
691
+ const toBeCopied = codeElem.textContent || "";
692
+ button.className = "md-clipboard md-icon";
693
+ button.title = "Copy to clipboard";
694
+ button.innerHTML = '<svg viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg>';
695
+ button.addEventListener("click", () => navigator.clipboard.writeText(toBeCopied));
696
+ (_a = codeElem == null ? void 0 : codeElem.parentElement) == null ? void 0 : _a.prepend(button);
697
+ });
698
+ return dom;
699
+ };
700
+ };
701
+
699
702
  const removeMkdocsHeader = () => {
700
703
  return (dom) => {
701
704
  var _a;
@@ -1330,6 +1333,8 @@ const useTechDocsReaderDom = (entityRef) => {
1330
1333
 
1331
1334
  --md-code-fg-color: ${theme.palette.text.primary};
1332
1335
  --md-code-bg-color: ${theme.palette.background.paper};
1336
+ --md-accent-fg-color: ${theme.palette.primary.main};
1337
+ --md-default-fg-color--lightest: ${theme.palette.textVerySubtle};
1333
1338
  }
1334
1339
  .md-main__inner { margin-top: 0; }
1335
1340
  .md-sidebar { position: fixed; bottom: 100px; width: 20rem; }
@@ -1342,6 +1347,13 @@ const useTechDocsReaderDom = (entityRef) => {
1342
1347
  .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; }
1343
1348
  .md-nav { font-size: 1rem; }
1344
1349
  .md-grid { max-width: 90vw; margin: 0 }
1350
+ .md-typeset blockquote {
1351
+ color: ${theme.palette.textSubtle};
1352
+ border-left: 0.2rem solid ${theme.palette.textVerySubtle};
1353
+ }
1354
+ .md-typeset hr {
1355
+ border-bottom: 0.05rem dotted ${theme.palette.textVerySubtle};
1356
+ }
1345
1357
  .md-typeset table:not([class]) {
1346
1358
  font-size: 1rem;
1347
1359
  border: 1px solid ${theme.palette.text.primary};
@@ -1452,11 +1464,14 @@ const useTechDocsReaderDom = (entityRef) => {
1452
1464
  theme.palette.primary.main,
1453
1465
  theme.palette.success.main,
1454
1466
  theme.palette.text.primary,
1467
+ theme.palette.textSubtle,
1468
+ theme.palette.textVerySubtle,
1455
1469
  theme.typography.fontFamily,
1456
1470
  isPinned
1457
1471
  ]);
1458
1472
  const postRender = useCallback(async (transformedElement) => transform(transformedElement, [
1459
1473
  scrollIntoAnchor(),
1474
+ copyToClipboard(),
1460
1475
  addLinkClickListener({
1461
1476
  baseUrl: window.location.origin,
1462
1477
  onClick: (event, url) => {