@backstage/frontend-app-api 0.7.5-next.3 → 0.9.0-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.
Files changed (73) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js +50 -0
  3. package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js.map +1 -0
  4. package/dist/extensions/Root.esm.js +19 -0
  5. package/dist/extensions/Root.esm.js.map +1 -0
  6. package/dist/frontend-plugin-api/src/wiring/createExtension.esm.js.map +1 -1
  7. package/dist/frontend-plugin-api/src/wiring/createExtensionOverrides.esm.js.map +1 -1
  8. package/dist/frontend-plugin-api/src/wiring/{createPlugin.esm.js → createFrontendPlugin.esm.js} +1 -1
  9. package/dist/frontend-plugin-api/src/wiring/createFrontendPlugin.esm.js.map +1 -0
  10. package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
  11. package/dist/index.d.ts +2 -10
  12. package/dist/tree/instantiateAppNodeTree.esm.js +8 -5
  13. package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
  14. package/dist/tree/resolveAppNodeSpecs.esm.js +1 -1
  15. package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
  16. package/dist/tree/resolveAppTree.esm.js +40 -20
  17. package/dist/tree/resolveAppTree.esm.js.map +1 -1
  18. package/dist/wiring/createApp.esm.js +114 -165
  19. package/dist/wiring/createApp.esm.js.map +1 -1
  20. package/package.json +7 -6
  21. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js +0 -30
  22. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js.map +0 -1
  23. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js +0 -15
  24. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js.map +0 -1
  25. package/dist/app/src/components/Root/LogoFull.esm.js +0 -33
  26. package/dist/app/src/components/Root/LogoFull.esm.js.map +0 -1
  27. package/dist/app/src/components/Root/LogoIcon.esm.js +0 -33
  28. package/dist/app/src/components/Root/LogoIcon.esm.js.map +0 -1
  29. package/dist/app-defaults/src/defaults/apis.esm.js +0 -221
  30. package/dist/app-defaults/src/defaults/apis.esm.js.map +0 -1
  31. package/dist/app-defaults/src/defaults/components.esm.js +0 -46
  32. package/dist/app-defaults/src/defaults/components.esm.js.map +0 -1
  33. package/dist/app-defaults/src/defaults/icons.esm.js +0 -51
  34. package/dist/app-defaults/src/defaults/icons.esm.js.map +0 -1
  35. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js +0 -89
  36. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js.map +0 -1
  37. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js +0 -76
  38. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js.map +0 -1
  39. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js +0 -234
  40. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +0 -1
  41. package/dist/core-app-api/src/app/AppThemeProvider.esm.js +0 -60
  42. package/dist/core-app-api/src/app/AppThemeProvider.esm.js.map +0 -1
  43. package/dist/core-app-api/src/app/isProtectedApp.esm.js +0 -8
  44. package/dist/core-app-api/src/app/isProtectedApp.esm.js.map +0 -1
  45. package/dist/core-app-api/src/lib/subjects.esm.js +0 -69
  46. package/dist/core-app-api/src/lib/subjects.esm.js.map +0 -1
  47. package/dist/core-plugin-api/src/translation/TranslationRef.esm.js +0 -13
  48. package/dist/core-plugin-api/src/translation/TranslationRef.esm.js.map +0 -1
  49. package/dist/core-plugin-api/src/translation/TranslationResource.esm.js +0 -13
  50. package/dist/core-plugin-api/src/translation/TranslationResource.esm.js.map +0 -1
  51. package/dist/extensions/App.esm.js +0 -42
  52. package/dist/extensions/App.esm.js.map +0 -1
  53. package/dist/extensions/AppLayout.esm.js +0 -34
  54. package/dist/extensions/AppLayout.esm.js.map +0 -1
  55. package/dist/extensions/AppNav.esm.js +0 -64
  56. package/dist/extensions/AppNav.esm.js.map +0 -1
  57. package/dist/extensions/AppRoot.esm.js +0 -124
  58. package/dist/extensions/AppRoot.esm.js.map +0 -1
  59. package/dist/extensions/AppRoutes.esm.js +0 -43
  60. package/dist/extensions/AppRoutes.esm.js.map +0 -1
  61. package/dist/extensions/components.esm.js +0 -52
  62. package/dist/extensions/components.esm.js.map +0 -1
  63. package/dist/extensions/elements.esm.js +0 -26
  64. package/dist/extensions/elements.esm.js.map +0 -1
  65. package/dist/extensions/themes.esm.js +0 -23
  66. package/dist/extensions/themes.esm.js.map +0 -1
  67. package/dist/frontend-plugin-api/src/wiring/createPlugin.esm.js.map +0 -1
  68. package/dist/routing/RouteTracker.esm.js +0 -69
  69. package/dist/routing/RouteTracker.esm.js.map +0 -1
  70. package/dist/tree/createAppTree.esm.js +0 -21
  71. package/dist/tree/createAppTree.esm.js.map +0 -1
  72. package/dist/wiring/InternalAppContext.esm.js +0 -6
  73. package/dist/wiring/InternalAppContext.esm.js.map +0 -1
@@ -1,46 +0,0 @@
1
- import React from 'react';
2
- import Button from '@material-ui/core/Button';
3
- import { Progress, ErrorPage, ErrorPanel } from '@backstage/core-components';
4
- import { BrowserRouter, useInRouterContext, MemoryRouter } from 'react-router-dom';
5
-
6
- function OptionallyWrapInRouter({ children }) {
7
- if (useInRouterContext()) {
8
- return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
9
- }
10
- return /* @__PURE__ */ React.createElement(MemoryRouter, null, children);
11
- }
12
- const DefaultNotFoundPage = () => /* @__PURE__ */ React.createElement(ErrorPage, { status: "404", statusMessage: "PAGE NOT FOUND" });
13
- const DefaultBootErrorPage = ({ step, error }) => {
14
- let message = "";
15
- if (step === "load-config") {
16
- message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;
17
- } else if (step === "load-chunk") {
18
- message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`;
19
- }
20
- return /* @__PURE__ */ React.createElement(OptionallyWrapInRouter, null, /* @__PURE__ */ React.createElement(ErrorPage, { statusMessage: message, stack: error.stack }));
21
- };
22
- const DefaultErrorBoundaryFallback = ({
23
- error,
24
- resetError,
25
- plugin
26
- }) => {
27
- return /* @__PURE__ */ React.createElement(
28
- ErrorPanel,
29
- {
30
- title: `Error in ${plugin?.getId()}`,
31
- defaultExpanded: true,
32
- error
33
- },
34
- /* @__PURE__ */ React.createElement(Button, { variant: "outlined", onClick: resetError }, "Retry")
35
- );
36
- };
37
- const components = {
38
- Progress,
39
- Router: BrowserRouter,
40
- NotFoundErrorPage: DefaultNotFoundPage,
41
- BootErrorPage: DefaultBootErrorPage,
42
- ErrorBoundaryFallback: DefaultErrorBoundaryFallback
43
- };
44
-
45
- export { OptionallyWrapInRouter, components };
46
- //# sourceMappingURL=components.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"components.esm.js","sources":["../../../../../app-defaults/src/defaults/components.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport Button from '@material-ui/core/Button';\nimport { ErrorPanel, Progress, ErrorPage } from '@backstage/core-components';\nimport {\n MemoryRouter,\n useInRouterContext,\n BrowserRouter,\n} from 'react-router-dom';\nimport {\n AppComponents,\n BootErrorPageProps,\n ErrorBoundaryFallbackProps,\n} from '@backstage/core-plugin-api';\n\nexport function OptionallyWrapInRouter({ children }: { children: ReactNode }) {\n if (useInRouterContext()) {\n return <>{children}</>;\n }\n return <MemoryRouter>{children}</MemoryRouter>;\n}\n\nconst DefaultNotFoundPage = () => (\n <ErrorPage status=\"404\" statusMessage=\"PAGE NOT FOUND\" />\n);\n\nconst DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => {\n let message = '';\n if (step === 'load-config') {\n message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;\n } else if (step === 'load-chunk') {\n message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`;\n }\n // TODO: figure out a nicer way to handle routing on the error page, when it can be done.\n return (\n <OptionallyWrapInRouter>\n <ErrorPage statusMessage={message} stack={error.stack} />\n </OptionallyWrapInRouter>\n );\n};\n\nconst DefaultErrorBoundaryFallback = ({\n error,\n resetError,\n plugin,\n}: ErrorBoundaryFallbackProps) => {\n return (\n <ErrorPanel\n title={`Error in ${plugin?.getId()}`}\n defaultExpanded\n error={error}\n >\n <Button variant=\"outlined\" onClick={resetError}>\n Retry\n </Button>\n </ErrorPanel>\n );\n};\n\n/**\n * Creates a set of default components to pass along to {@link @backstage/core-app-api#createSpecializedApp}.\n *\n * @public\n */\nexport const components: AppComponents = {\n Progress,\n Router: BrowserRouter,\n NotFoundErrorPage: DefaultNotFoundPage,\n BootErrorPage: DefaultBootErrorPage,\n ErrorBoundaryFallback: DefaultErrorBoundaryFallback,\n};\n"],"names":[],"mappings":";;;;;AA8BgB,SAAA,sBAAA,CAAuB,EAAE,QAAA,EAAqC,EAAA;AAC5E,EAAA,IAAI,oBAAsB,EAAA;AACxB,IAAA,iEAAU,QAAS,CAAA,CAAA;AAAA,GACrB;AACA,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,oBAAc,QAAS,CAAA,CAAA;AACjC,CAAA;AAEA,MAAM,sBAAsB,sBAC1B,KAAA,CAAA,aAAA,CAAC,aAAU,MAAO,EAAA,KAAA,EAAM,eAAc,gBAAiB,EAAA,CAAA,CAAA;AAGzD,MAAM,oBAAuB,GAAA,CAAC,EAAE,IAAA,EAAM,OAAgC,KAAA;AACpE,EAAA,IAAI,OAAU,GAAA,EAAA,CAAA;AACd,EAAA,IAAI,SAAS,aAAe,EAAA;AAC1B,IAAU,OAAA,GAAA,CAAA,4EAAA,EAA+E,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACxG,MAAA,IAAW,SAAS,YAAc,EAAA;AAChC,IAAU,OAAA,GAAA,CAAA,0DAAA,EAA6D,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACtF;AAEA,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,8CACE,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,EAAU,eAAe,OAAS,EAAA,KAAA,EAAO,KAAM,CAAA,KAAA,EAAO,CACzD,CAAA,CAAA;AAEJ,CAAA,CAAA;AAEA,MAAM,+BAA+B,CAAC;AAAA,EACpC,KAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AACF,CAAkC,KAAA;AAChC,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,KAAO,EAAA,CAAA,SAAA,EAAY,MAAQ,EAAA,KAAA,EAAO,CAAA,CAAA;AAAA,MAClC,eAAe,EAAA,IAAA;AAAA,MACf,KAAA;AAAA,KAAA;AAAA,wCAEC,MAAO,EAAA,EAAA,OAAA,EAAQ,UAAW,EAAA,OAAA,EAAS,cAAY,OAEhD,CAAA;AAAA,GACF,CAAA;AAEJ,CAAA,CAAA;AAOO,MAAM,UAA4B,GAAA;AAAA,EACvC,QAAA;AAAA,EACA,MAAQ,EAAA,aAAA;AAAA,EACR,iBAAmB,EAAA,mBAAA;AAAA,EACnB,aAAe,EAAA,oBAAA;AAAA,EACf,qBAAuB,EAAA,4BAAA;AACzB;;;;"}
@@ -1,51 +0,0 @@
1
- import MuiApartmentIcon from '@material-ui/icons/Apartment';
2
- import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage';
3
- import MuiCategoryIcon from '@material-ui/icons/Category';
4
- import MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder';
5
- import MuiSubjectIcon from '@material-ui/icons/Subject';
6
- import MuiSearchIcon from '@material-ui/icons/Search';
7
- import MuiChatIcon from '@material-ui/icons/Chat';
8
- import MuiDashboardIcon from '@material-ui/icons/Dashboard';
9
- import MuiDocsIcon from '@material-ui/icons/Description';
10
- import MuiEmailIcon from '@material-ui/icons/Email';
11
- import MuiExtensionIcon from '@material-ui/icons/Extension';
12
- import MuiGitHubIcon from '@material-ui/icons/GitHub';
13
- import MuiHelpIcon from '@material-ui/icons/Help';
14
- import MuiLocationOnIcon from '@material-ui/icons/LocationOn';
15
- import MuiMemoryIcon from '@material-ui/icons/Memory';
16
- import MuiMenuBookIcon from '@material-ui/icons/MenuBook';
17
- import MuiPeopleIcon from '@material-ui/icons/People';
18
- import MuiPersonIcon from '@material-ui/icons/Person';
19
- import MuiWarningIcon from '@material-ui/icons/Warning';
20
- import MuiStorageIcon from '@material-ui/icons/Storage';
21
- import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList';
22
-
23
- const icons = {
24
- brokenImage: MuiBrokenImageIcon,
25
- // To be confirmed: see https://github.com/backstage/backstage/issues/4970
26
- catalog: MuiMenuBookIcon,
27
- scaffolder: MuiCreateNewFolderIcon,
28
- techdocs: MuiSubjectIcon,
29
- search: MuiSearchIcon,
30
- chat: MuiChatIcon,
31
- dashboard: MuiDashboardIcon,
32
- docs: MuiDocsIcon,
33
- email: MuiEmailIcon,
34
- github: MuiGitHubIcon,
35
- group: MuiPeopleIcon,
36
- help: MuiHelpIcon,
37
- "kind:api": MuiExtensionIcon,
38
- "kind:component": MuiMemoryIcon,
39
- "kind:domain": MuiApartmentIcon,
40
- "kind:group": MuiPeopleIcon,
41
- "kind:location": MuiLocationOnIcon,
42
- "kind:system": MuiCategoryIcon,
43
- "kind:user": MuiPersonIcon,
44
- "kind:resource": MuiStorageIcon,
45
- "kind:template": MuiFeaturedPlayListIcon,
46
- user: MuiPersonIcon,
47
- warning: MuiWarningIcon
48
- };
49
-
50
- export { icons };
51
- //# sourceMappingURL=icons.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"icons.esm.js","sources":["../../../../../app-defaults/src/defaults/icons.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { IconComponent } from '@backstage/core-plugin-api';\nimport MuiApartmentIcon from '@material-ui/icons/Apartment';\nimport MuiBrokenImageIcon from '@material-ui/icons/BrokenImage';\nimport MuiCategoryIcon from '@material-ui/icons/Category';\nimport MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder';\nimport MuiSubjectIcon from '@material-ui/icons/Subject';\nimport MuiSearchIcon from '@material-ui/icons/Search';\nimport MuiChatIcon from '@material-ui/icons/Chat';\nimport MuiDashboardIcon from '@material-ui/icons/Dashboard';\nimport MuiDocsIcon from '@material-ui/icons/Description';\nimport MuiEmailIcon from '@material-ui/icons/Email';\nimport MuiExtensionIcon from '@material-ui/icons/Extension';\nimport MuiGitHubIcon from '@material-ui/icons/GitHub';\nimport MuiHelpIcon from '@material-ui/icons/Help';\nimport MuiLocationOnIcon from '@material-ui/icons/LocationOn';\nimport MuiMemoryIcon from '@material-ui/icons/Memory';\nimport MuiMenuBookIcon from '@material-ui/icons/MenuBook';\nimport MuiPeopleIcon from '@material-ui/icons/People';\nimport MuiPersonIcon from '@material-ui/icons/Person';\nimport MuiWarningIcon from '@material-ui/icons/Warning';\nimport MuiStorageIcon from '@material-ui/icons/Storage';\nimport MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList';\n\nexport const icons = {\n brokenImage: MuiBrokenImageIcon as IconComponent,\n // To be confirmed: see https://github.com/backstage/backstage/issues/4970\n catalog: MuiMenuBookIcon as IconComponent,\n scaffolder: MuiCreateNewFolderIcon as IconComponent,\n techdocs: MuiSubjectIcon as IconComponent,\n search: MuiSearchIcon as IconComponent,\n chat: MuiChatIcon as IconComponent,\n dashboard: MuiDashboardIcon as IconComponent,\n docs: MuiDocsIcon as IconComponent,\n email: MuiEmailIcon as IconComponent,\n github: MuiGitHubIcon as IconComponent,\n group: MuiPeopleIcon as IconComponent,\n help: MuiHelpIcon as IconComponent,\n 'kind:api': MuiExtensionIcon as IconComponent,\n 'kind:component': MuiMemoryIcon as IconComponent,\n 'kind:domain': MuiApartmentIcon as IconComponent,\n 'kind:group': MuiPeopleIcon as IconComponent,\n 'kind:location': MuiLocationOnIcon as IconComponent,\n 'kind:system': MuiCategoryIcon as IconComponent,\n 'kind:user': MuiPersonIcon as IconComponent,\n 'kind:resource': MuiStorageIcon as IconComponent,\n 'kind:template': MuiFeaturedPlayListIcon as IconComponent,\n user: MuiPersonIcon as IconComponent,\n warning: MuiWarningIcon as IconComponent,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuCO,MAAM,KAAQ,GAAA;AAAA,EACnB,WAAa,EAAA,kBAAA;AAAA;AAAA,EAEb,OAAS,EAAA,eAAA;AAAA,EACT,UAAY,EAAA,sBAAA;AAAA,EACZ,QAAU,EAAA,cAAA;AAAA,EACV,MAAQ,EAAA,aAAA;AAAA,EACR,IAAM,EAAA,WAAA;AAAA,EACN,SAAW,EAAA,gBAAA;AAAA,EACX,IAAM,EAAA,WAAA;AAAA,EACN,KAAO,EAAA,YAAA;AAAA,EACP,MAAQ,EAAA,aAAA;AAAA,EACR,KAAO,EAAA,aAAA;AAAA,EACP,IAAM,EAAA,WAAA;AAAA,EACN,UAAY,EAAA,gBAAA;AAAA,EACZ,gBAAkB,EAAA,aAAA;AAAA,EAClB,aAAe,EAAA,gBAAA;AAAA,EACf,YAAc,EAAA,aAAA;AAAA,EACd,eAAiB,EAAA,iBAAA;AAAA,EACjB,aAAe,EAAA,eAAA;AAAA,EACf,WAAa,EAAA,aAAA;AAAA,EACb,eAAiB,EAAA,cAAA;AAAA,EACjB,eAAiB,EAAA,uBAAA;AAAA,EACjB,IAAM,EAAA,aAAA;AAAA,EACN,OAAS,EAAA,cAAA;AACX;;;;"}
@@ -1,89 +0,0 @@
1
- import { BehaviorSubject } from '../../../lib/subjects.esm.js';
2
- import '@backstage/core-plugin-api';
3
-
4
- const STORAGE_KEY = "language";
5
- const DEFAULT_LANGUAGE = "en";
6
- class AppLanguageSelector {
7
- static create(options) {
8
- const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];
9
- if (languages.length !== new Set(languages).size) {
10
- throw new Error(
11
- `Supported languages may not contain duplicates, got '${languages.join(
12
- "', '"
13
- )}'`
14
- );
15
- }
16
- if (!languages.includes(DEFAULT_LANGUAGE)) {
17
- throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);
18
- }
19
- const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;
20
- if (!languages.includes(initialLanguage)) {
21
- throw new Error(
22
- `Initial language must be one of the supported languages, got '${initialLanguage}'`
23
- );
24
- }
25
- return new AppLanguageSelector(languages, initialLanguage);
26
- }
27
- static createWithStorage(options) {
28
- const selector = AppLanguageSelector.create(options);
29
- if (!window.localStorage) {
30
- return selector;
31
- }
32
- const storedLanguage = window.localStorage.getItem(STORAGE_KEY);
33
- const { languages } = selector.getAvailableLanguages();
34
- if (storedLanguage && languages.includes(storedLanguage)) {
35
- selector.setLanguage(storedLanguage);
36
- }
37
- selector.language$().subscribe(({ language }) => {
38
- if (language !== window.localStorage.getItem(STORAGE_KEY)) {
39
- window.localStorage.setItem(STORAGE_KEY, language);
40
- }
41
- });
42
- window.addEventListener("storage", (event) => {
43
- if (event.key === STORAGE_KEY) {
44
- const language = localStorage.getItem(STORAGE_KEY) ?? void 0;
45
- if (language) {
46
- selector.setLanguage(language);
47
- }
48
- }
49
- });
50
- return selector;
51
- }
52
- #languages;
53
- #language;
54
- #subject;
55
- constructor(languages, initialLanguage) {
56
- this.#languages = languages;
57
- this.#language = initialLanguage;
58
- this.#subject = new BehaviorSubject({
59
- language: this.#language
60
- });
61
- }
62
- getAvailableLanguages() {
63
- return { languages: this.#languages.slice() };
64
- }
65
- setLanguage(language) {
66
- const lng = language ?? DEFAULT_LANGUAGE;
67
- if (lng === this.#language) {
68
- return;
69
- }
70
- if (lng && !this.#languages.includes(lng)) {
71
- throw new Error(
72
- `Failed to change language to '${lng}', available languages are '${this.#languages.join(
73
- "', '"
74
- )}'`
75
- );
76
- }
77
- this.#language = lng;
78
- this.#subject.next({ language: lng });
79
- }
80
- getLanguage() {
81
- return { language: this.#language };
82
- }
83
- language$() {
84
- return this.#subject;
85
- }
86
- }
87
-
88
- export { AppLanguageSelector, DEFAULT_LANGUAGE };
89
- //# sourceMappingURL=AppLanguageSelector.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AppLanguageSelector.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageApi } from '@backstage/core-plugin-api/alpha';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib';\n\nconst STORAGE_KEY = 'language';\nexport const DEFAULT_LANGUAGE = 'en';\n\n/** @alpha */\nexport interface AppLanguageSelectorOptions {\n defaultLanguage?: string;\n availableLanguages?: string[];\n}\n\n/**\n * Exposes the available languages in the app and allows for switching of the active language.\n *\n * @alpha\n */\nexport class AppLanguageSelector implements AppLanguageApi {\n static create(options?: AppLanguageSelectorOptions) {\n const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];\n if (languages.length !== new Set(languages).size) {\n throw new Error(\n `Supported languages may not contain duplicates, got '${languages.join(\n \"', '\",\n )}'`,\n );\n }\n if (!languages.includes(DEFAULT_LANGUAGE)) {\n throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);\n }\n\n const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;\n if (!languages.includes(initialLanguage)) {\n throw new Error(\n `Initial language must be one of the supported languages, got '${initialLanguage}'`,\n );\n }\n\n return new AppLanguageSelector(languages, initialLanguage);\n }\n\n static createWithStorage(options?: AppLanguageSelectorOptions) {\n const selector = AppLanguageSelector.create(options);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const storedLanguage = window.localStorage.getItem(STORAGE_KEY);\n const { languages } = selector.getAvailableLanguages();\n if (storedLanguage && languages.includes(storedLanguage)) {\n selector.setLanguage(storedLanguage);\n }\n\n selector.language$().subscribe(({ language }) => {\n if (language !== window.localStorage.getItem(STORAGE_KEY)) {\n window.localStorage.setItem(STORAGE_KEY, language);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const language = localStorage.getItem(STORAGE_KEY) ?? undefined;\n if (language) {\n selector.setLanguage(language);\n }\n }\n });\n\n return selector;\n }\n\n #languages: string[];\n #language: string;\n #subject: BehaviorSubject<{ language: string }>;\n\n private constructor(languages: string[], initialLanguage: string) {\n this.#languages = languages;\n this.#language = initialLanguage;\n this.#subject = new BehaviorSubject<{ language: string }>({\n language: this.#language,\n });\n }\n\n getAvailableLanguages(): { languages: string[] } {\n return { languages: this.#languages.slice() };\n }\n\n setLanguage(language?: string | undefined): void {\n const lng = language ?? DEFAULT_LANGUAGE;\n if (lng === this.#language) {\n return;\n }\n if (lng && !this.#languages.includes(lng)) {\n throw new Error(\n `Failed to change language to '${lng}', available languages are '${this.#languages.join(\n \"', '\",\n )}'`,\n );\n }\n this.#language = lng;\n this.#subject.next({ language: lng });\n }\n\n getLanguage(): { language: string } {\n return { language: this.#language };\n }\n\n language$(): Observable<{ language: string }> {\n return this.#subject;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,76 +0,0 @@
1
- import { FeatureFlagState } from '@backstage/core-plugin-api';
2
-
3
- function validateFlagName(name) {
4
- if (name.length < 3) {
5
- throw new Error(
6
- `The '${name}' feature flag must have a minimum length of three characters.`
7
- );
8
- }
9
- if (name.length > 150) {
10
- throw new Error(
11
- `The '${name}' feature flag must not exceed 150 characters.`
12
- );
13
- }
14
- if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {
15
- throw new Error(
16
- `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. Examples: feature-flag-one, alpha, release-2020`
17
- );
18
- }
19
- }
20
- class LocalStorageFeatureFlags {
21
- registeredFeatureFlags = [];
22
- flags;
23
- registerFlag(flag) {
24
- validateFlagName(flag.name);
25
- this.registeredFeatureFlags.push(flag);
26
- }
27
- getRegisteredFlags() {
28
- return this.registeredFeatureFlags.slice();
29
- }
30
- isActive(name) {
31
- if (!this.flags) {
32
- this.flags = this.load();
33
- }
34
- return this.flags.get(name) === FeatureFlagState.Active;
35
- }
36
- save(options) {
37
- if (!this.flags) {
38
- this.flags = this.load();
39
- }
40
- if (!options.merge) {
41
- this.flags.clear();
42
- }
43
- for (const [name, state] of Object.entries(options.states)) {
44
- this.flags.set(name, state);
45
- }
46
- const enabled = Array.from(this.flags.entries()).filter(
47
- ([, state]) => state === FeatureFlagState.Active
48
- );
49
- window.localStorage.setItem(
50
- "featureFlags",
51
- JSON.stringify(Object.fromEntries(enabled))
52
- );
53
- }
54
- load() {
55
- try {
56
- const jsonStr = window.localStorage.getItem("featureFlags");
57
- if (!jsonStr) {
58
- return /* @__PURE__ */ new Map();
59
- }
60
- const json = JSON.parse(jsonStr);
61
- if (typeof json !== "object" || json === null || Array.isArray(json)) {
62
- return /* @__PURE__ */ new Map();
63
- }
64
- const entries = Object.entries(json).filter(([name, value]) => {
65
- validateFlagName(name);
66
- return value === FeatureFlagState.Active;
67
- });
68
- return new Map(entries);
69
- } catch {
70
- return /* @__PURE__ */ new Map();
71
- }
72
- }
73
- }
74
-
75
- export { LocalStorageFeatureFlags, validateFlagName };
76
- //# sourceMappingURL=LocalStorageFeatureFlags.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LocalStorageFeatureFlags.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FeatureFlagState,\n FeatureFlagsApi,\n FeatureFlag,\n FeatureFlagsSaveOptions,\n} from '@backstage/core-plugin-api';\n\nexport function validateFlagName(name: string): void {\n if (name.length < 3) {\n throw new Error(\n `The '${name}' feature flag must have a minimum length of three characters.`,\n );\n }\n\n if (name.length > 150) {\n throw new Error(\n `The '${name}' feature flag must not exceed 150 characters.`,\n );\n }\n\n if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {\n throw new Error(\n `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ` +\n 'Examples: feature-flag-one, alpha, release-2020',\n );\n }\n}\n\n/**\n * A feature flags implementation that stores the flags in the browser's local\n * storage.\n *\n * @public\n */\nexport class LocalStorageFeatureFlags implements FeatureFlagsApi {\n private registeredFeatureFlags: FeatureFlag[] = [];\n private flags?: Map<string, FeatureFlagState>;\n\n registerFlag(flag: FeatureFlag) {\n validateFlagName(flag.name);\n this.registeredFeatureFlags.push(flag);\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFeatureFlags.slice();\n }\n\n isActive(name: string): boolean {\n if (!this.flags) {\n this.flags = this.load();\n }\n return this.flags.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (!this.flags) {\n this.flags = this.load();\n }\n if (!options.merge) {\n this.flags.clear();\n }\n for (const [name, state] of Object.entries(options.states)) {\n this.flags.set(name, state);\n }\n\n const enabled = Array.from(this.flags.entries()).filter(\n ([, state]) => state === FeatureFlagState.Active,\n );\n window.localStorage.setItem(\n 'featureFlags',\n JSON.stringify(Object.fromEntries(enabled)),\n );\n }\n\n private load(): Map<string, FeatureFlagState> {\n try {\n const jsonStr = window.localStorage.getItem('featureFlags');\n if (!jsonStr) {\n return new Map();\n }\n const json = JSON.parse(jsonStr) as unknown;\n if (typeof json !== 'object' || json === null || Array.isArray(json)) {\n return new Map();\n }\n\n const entries = Object.entries(json).filter(([name, value]) => {\n validateFlagName(name);\n return value === FeatureFlagState.Active;\n });\n\n return new Map(entries);\n } catch {\n return new Map();\n }\n }\n}\n"],"names":[],"mappings":";;AAuBO,SAAS,iBAAiB,IAAoB,EAAA;AACnD,EAAI,IAAA,IAAA,CAAK,SAAS,CAAG,EAAA;AACnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,QAAQ,IAAI,CAAA,8DAAA,CAAA;AAAA,KACd,CAAA;AAAA,GACF;AAEA,EAAI,IAAA,IAAA,CAAK,SAAS,GAAK,EAAA;AACrB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,QAAQ,IAAI,CAAA,8CAAA,CAAA;AAAA,KACd,CAAA;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,IAAA,CAAK,KAAM,CAAA,oBAAoB,CAAG,EAAA;AACrC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,QAAQ,IAAI,CAAA,0JAAA,CAAA;AAAA,KAEd,CAAA;AAAA,GACF;AACF,CAAA;AAQO,MAAM,wBAAoD,CAAA;AAAA,EACvD,yBAAwC,EAAC,CAAA;AAAA,EACzC,KAAA,CAAA;AAAA,EAER,aAAa,IAAmB,EAAA;AAC9B,IAAA,gBAAA,CAAiB,KAAK,IAAI,CAAA,CAAA;AAC1B,IAAK,IAAA,CAAA,sBAAA,CAAuB,KAAK,IAAI,CAAA,CAAA;AAAA,GACvC;AAAA,EAEA,kBAAoC,GAAA;AAClC,IAAO,OAAA,IAAA,CAAK,uBAAuB,KAAM,EAAA,CAAA;AAAA,GAC3C;AAAA,EAEA,SAAS,IAAuB,EAAA;AAC9B,IAAI,IAAA,CAAC,KAAK,KAAO,EAAA;AACf,MAAK,IAAA,CAAA,KAAA,GAAQ,KAAK,IAAK,EAAA,CAAA;AAAA,KACzB;AACA,IAAA,OAAO,IAAK,CAAA,KAAA,CAAM,GAAI,CAAA,IAAI,MAAM,gBAAiB,CAAA,MAAA,CAAA;AAAA,GACnD;AAAA,EAEA,KAAK,OAAwC,EAAA;AAC3C,IAAI,IAAA,CAAC,KAAK,KAAO,EAAA;AACf,MAAK,IAAA,CAAA,KAAA,GAAQ,KAAK,IAAK,EAAA,CAAA;AAAA,KACzB;AACA,IAAI,IAAA,CAAC,QAAQ,KAAO,EAAA;AAClB,MAAA,IAAA,CAAK,MAAM,KAAM,EAAA,CAAA;AAAA,KACnB;AACA,IAAW,KAAA,MAAA,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAQ,CAAA,OAAA,CAAQ,MAAM,CAAG,EAAA;AAC1D,MAAK,IAAA,CAAA,KAAA,CAAM,GAAI,CAAA,IAAA,EAAM,KAAK,CAAA,CAAA;AAAA,KAC5B;AAEA,IAAA,MAAM,UAAU,KAAM,CAAA,IAAA,CAAK,KAAK,KAAM,CAAA,OAAA,EAAS,CAAE,CAAA,MAAA;AAAA,MAC/C,CAAC,GAAG,KAAK,CAAA,KAAM,UAAU,gBAAiB,CAAA,MAAA;AAAA,KAC5C,CAAA;AACA,IAAA,MAAA,CAAO,YAAa,CAAA,OAAA;AAAA,MAClB,cAAA;AAAA,MACA,IAAK,CAAA,SAAA,CAAU,MAAO,CAAA,WAAA,CAAY,OAAO,CAAC,CAAA;AAAA,KAC5C,CAAA;AAAA,GACF;AAAA,EAEQ,IAAsC,GAAA;AAC5C,IAAI,IAAA;AACF,MAAA,MAAM,OAAU,GAAA,MAAA,CAAO,YAAa,CAAA,OAAA,CAAQ,cAAc,CAAA,CAAA;AAC1D,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,2BAAW,GAAI,EAAA,CAAA;AAAA,OACjB;AACA,MAAM,MAAA,IAAA,GAAO,IAAK,CAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAC/B,MAAI,IAAA,OAAO,SAAS,QAAY,IAAA,IAAA,KAAS,QAAQ,KAAM,CAAA,OAAA,CAAQ,IAAI,CAAG,EAAA;AACpE,QAAA,2BAAW,GAAI,EAAA,CAAA;AAAA,OACjB;AAEA,MAAM,MAAA,OAAA,GAAU,MAAO,CAAA,OAAA,CAAQ,IAAI,CAAA,CAAE,OAAO,CAAC,CAAC,IAAM,EAAA,KAAK,CAAM,KAAA;AAC7D,QAAA,gBAAA,CAAiB,IAAI,CAAA,CAAA;AACrB,QAAA,OAAO,UAAU,gBAAiB,CAAA,MAAA,CAAA;AAAA,OACnC,CAAA,CAAA;AAED,MAAO,OAAA,IAAI,IAAI,OAAO,CAAA,CAAA;AAAA,KAChB,CAAA,MAAA;AACN,MAAA,2BAAW,GAAI,EAAA,CAAA;AAAA,KACjB;AAAA,GACF;AACF;;;;"}
@@ -1,234 +0,0 @@
1
- import { createInstance } from 'i18next';
2
- import ObservableImpl from 'zen-observable';
3
- import { toInternalTranslationResource } from '../../../../../core-plugin-api/src/translation/TranslationResource.esm.js';
4
- import { toInternalTranslationRef } from '../../../../../core-plugin-api/src/translation/TranslationRef.esm.js';
5
- import { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector.esm.js';
6
-
7
- function removeNulls(messages) {
8
- return Object.fromEntries(
9
- Object.entries(messages).filter(
10
- (e) => e[1] !== null
11
- )
12
- );
13
- }
14
- class ResourceLoader {
15
- constructor(onLoad) {
16
- this.onLoad = onLoad;
17
- }
18
- /** Loaded resources by loader key */
19
- #loaded = /* @__PURE__ */ new Set();
20
- /** Resource loading promises by loader key */
21
- #loading = /* @__PURE__ */ new Map();
22
- /** Loaders for each resource language */
23
- #loaders = /* @__PURE__ */ new Map();
24
- addTranslationResource(resource) {
25
- const internalResource = toInternalTranslationResource(resource);
26
- for (const entry of internalResource.resources) {
27
- const key = this.#getLoaderKey(entry.language, internalResource.id);
28
- if (!this.#loaders.has(key)) {
29
- this.#loaders.set(key, entry.loader);
30
- }
31
- }
32
- }
33
- #getLoaderKey(language, namespace) {
34
- return `${language}/${namespace}`;
35
- }
36
- needsLoading(language, namespace) {
37
- const key = this.#getLoaderKey(language, namespace);
38
- const loader = this.#loaders.get(key);
39
- if (!loader) {
40
- return false;
41
- }
42
- return !this.#loaded.has(key);
43
- }
44
- async load(language, namespace) {
45
- const key = this.#getLoaderKey(language, namespace);
46
- const loader = this.#loaders.get(key);
47
- if (!loader) {
48
- return;
49
- }
50
- if (this.#loaded.has(key)) {
51
- return;
52
- }
53
- const loading = this.#loading.get(key);
54
- if (loading) {
55
- await loading;
56
- return;
57
- }
58
- const load = loader().then(
59
- (result) => {
60
- this.onLoad({ language, namespace, messages: result.messages });
61
- this.#loaded.add(key);
62
- },
63
- (error) => {
64
- this.#loaded.add(key);
65
- throw error;
66
- }
67
- );
68
- this.#loading.set(key, load);
69
- await load;
70
- }
71
- }
72
- class I18nextTranslationApi {
73
- static create(options) {
74
- const { languages } = options.languageApi.getAvailableLanguages();
75
- const i18n = createInstance({
76
- fallbackLng: DEFAULT_LANGUAGE,
77
- supportedLngs: languages,
78
- interpolation: {
79
- escapeValue: false
80
- },
81
- ns: [],
82
- defaultNS: false,
83
- fallbackNS: false,
84
- // Disable resource loading on init, meaning i18n will be ready to use immediately
85
- initImmediate: false
86
- });
87
- i18n.init();
88
- if (!i18n.isInitialized) {
89
- throw new Error("i18next was unexpectedly not initialized");
90
- }
91
- const { language: initialLanguage } = options.languageApi.getLanguage();
92
- if (initialLanguage !== DEFAULT_LANGUAGE) {
93
- i18n.changeLanguage(initialLanguage);
94
- }
95
- const loader = new ResourceLoader((loaded) => {
96
- i18n.addResourceBundle(
97
- loaded.language,
98
- loaded.namespace,
99
- removeNulls(loaded.messages),
100
- false,
101
- // do not merge with existing translations
102
- true
103
- // overwrite translations
104
- );
105
- });
106
- const resources = options?.resources || [];
107
- for (let i = resources.length - 1; i >= 0; i--) {
108
- const resource = resources[i];
109
- if (resource.$$type === "@backstage/TranslationResource") {
110
- loader.addTranslationResource(resource);
111
- } else if (resource.$$type === "@backstage/TranslationMessages") {
112
- i18n.addResourceBundle(
113
- DEFAULT_LANGUAGE,
114
- resource.id,
115
- removeNulls(resource.messages),
116
- true,
117
- // merge with existing translations
118
- false
119
- // do not overwrite translations
120
- );
121
- }
122
- }
123
- const instance = new I18nextTranslationApi(
124
- i18n,
125
- loader,
126
- options.languageApi.getLanguage().language
127
- );
128
- options.languageApi.language$().subscribe(({ language }) => {
129
- instance.#changeLanguage(language);
130
- });
131
- return instance;
132
- }
133
- #i18n;
134
- #loader;
135
- #language;
136
- /** Keep track of which refs we have registered default resources for */
137
- #registeredRefs = /* @__PURE__ */ new Set();
138
- /** Notify observers when language changes */
139
- #languageChangeListeners = /* @__PURE__ */ new Set();
140
- constructor(i18n, loader, language) {
141
- this.#i18n = i18n;
142
- this.#loader = loader;
143
- this.#language = language;
144
- }
145
- getTranslation(translationRef) {
146
- const internalRef = toInternalTranslationRef(translationRef);
147
- this.#registerDefaults(internalRef);
148
- return this.#createSnapshot(internalRef);
149
- }
150
- translation$(translationRef) {
151
- const internalRef = toInternalTranslationRef(translationRef);
152
- this.#registerDefaults(internalRef);
153
- return new ObservableImpl((subscriber) => {
154
- let loadTicket = {};
155
- const loadResource = () => {
156
- loadTicket = {};
157
- const ticket = loadTicket;
158
- this.#loader.load(this.#language, internalRef.id).then(
159
- () => {
160
- if (ticket === loadTicket) {
161
- const snapshot = this.#createSnapshot(internalRef);
162
- if (snapshot.ready) {
163
- subscriber.next(snapshot);
164
- }
165
- }
166
- },
167
- (error) => {
168
- if (ticket === loadTicket) {
169
- subscriber.error(Array.isArray(error) ? error[0] : error);
170
- }
171
- }
172
- );
173
- };
174
- const onChange = () => {
175
- const snapshot = this.#createSnapshot(internalRef);
176
- if (snapshot.ready) {
177
- subscriber.next(snapshot);
178
- } else {
179
- loadResource();
180
- }
181
- };
182
- if (this.#loader.needsLoading(this.#language, internalRef.id)) {
183
- loadResource();
184
- }
185
- this.#languageChangeListeners.add(onChange);
186
- return () => {
187
- this.#languageChangeListeners.delete(onChange);
188
- };
189
- });
190
- }
191
- #changeLanguage(language) {
192
- if (this.#language !== language) {
193
- this.#language = language;
194
- this.#i18n.changeLanguage(language);
195
- this.#languageChangeListeners.forEach((listener) => listener());
196
- }
197
- }
198
- #createSnapshot(internalRef) {
199
- if (this.#loader.needsLoading(this.#language, internalRef.id)) {
200
- return { ready: false };
201
- }
202
- const t = this.#i18n.getFixedT(
203
- null,
204
- internalRef.id
205
- );
206
- return {
207
- ready: true,
208
- t
209
- };
210
- }
211
- #registerDefaults(internalRef) {
212
- if (this.#registeredRefs.has(internalRef.id)) {
213
- return;
214
- }
215
- this.#registeredRefs.add(internalRef.id);
216
- const defaultMessages = internalRef.getDefaultMessages();
217
- this.#i18n.addResourceBundle(
218
- DEFAULT_LANGUAGE,
219
- internalRef.id,
220
- defaultMessages,
221
- true,
222
- // merge with existing translations
223
- false
224
- // do not overwrite translations
225
- );
226
- const defaultResource = internalRef.getDefaultResource();
227
- if (defaultResource) {
228
- this.#loader.addTranslationResource(defaultResource);
229
- }
230
- }
231
- }
232
-
233
- export { I18nextTranslationApi };
234
- //# sourceMappingURL=I18nextTranslationApi.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"I18nextTranslationApi.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppLanguageApi,\n TranslationApi,\n TranslationFunction,\n TranslationMessages,\n TranslationRef,\n TranslationResource,\n TranslationSnapshot,\n} from '@backstage/core-plugin-api/alpha';\nimport { createInstance as createI18n, type i18n as I18n } from 'i18next';\nimport ObservableImpl from 'zen-observable';\n\n// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalTranslationResource,\n InternalTranslationResourceLoader,\n} from '../../../../../core-plugin-api/src/translation/TranslationResource';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalTranslationRef,\n InternalTranslationRef,\n} from '../../../../../core-plugin-api/src/translation/TranslationRef';\nimport { Observable } from '@backstage/types';\nimport { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector';\n\n/** @alpha */\nexport interface I18nextTranslationApiOptions {\n languageApi: AppLanguageApi;\n resources?: Array<TranslationMessages | TranslationResource>;\n}\n\nfunction removeNulls(\n messages: Record<string, string | null>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(messages).filter(\n (e): e is [string, string] => e[1] !== null,\n ),\n );\n}\n\n/**\n * The built-in i18next backend loading logic doesn't handle on the fly switches\n * of language very well. It gets a bit confused about whether resources are actually\n * loaded or not, so instead we implement our own resource loader.\n */\nclass ResourceLoader {\n /** Loaded resources by loader key */\n #loaded = new Set<string>();\n /** Resource loading promises by loader key */\n #loading = new Map<string, Promise<void>>();\n /** Loaders for each resource language */\n #loaders = new Map<string, InternalTranslationResourceLoader>();\n\n constructor(\n private readonly onLoad: (loaded: {\n language: string;\n namespace: string;\n messages: Record<string, string | null>;\n }) => void,\n ) {}\n\n addTranslationResource(resource: TranslationResource) {\n const internalResource = toInternalTranslationResource(resource);\n for (const entry of internalResource.resources) {\n const key = this.#getLoaderKey(entry.language, internalResource.id);\n\n // First loader to register wins, this means that resources registered in the app\n // have priority over default resource from translation refs\n if (!this.#loaders.has(key)) {\n this.#loaders.set(key, entry.loader);\n }\n }\n }\n\n #getLoaderKey(language: string, namespace: string) {\n return `${language}/${namespace}`;\n }\n\n needsLoading(language: string, namespace: string) {\n const key = this.#getLoaderKey(language, namespace);\n const loader = this.#loaders.get(key);\n if (!loader) {\n return false;\n }\n\n return !this.#loaded.has(key);\n }\n\n async load(language: string, namespace: string): Promise<void> {\n const key = this.#getLoaderKey(language, namespace);\n\n const loader = this.#loaders.get(key);\n if (!loader) {\n return;\n }\n\n if (this.#loaded.has(key)) {\n return;\n }\n\n const loading = this.#loading.get(key);\n if (loading) {\n await loading;\n return;\n }\n\n const load = loader().then(\n result => {\n this.onLoad({ language, namespace, messages: result.messages });\n this.#loaded.add(key);\n },\n error => {\n this.#loaded.add(key); // Do not try to load failed resources again\n throw error;\n },\n );\n this.#loading.set(key, load);\n await load;\n }\n}\n\n/** @alpha */\nexport class I18nextTranslationApi implements TranslationApi {\n static create(options: I18nextTranslationApiOptions) {\n const { languages } = options.languageApi.getAvailableLanguages();\n\n const i18n = createI18n({\n fallbackLng: DEFAULT_LANGUAGE,\n supportedLngs: languages,\n interpolation: {\n escapeValue: false,\n },\n ns: [],\n defaultNS: false,\n fallbackNS: false,\n\n // Disable resource loading on init, meaning i18n will be ready to use immediately\n initImmediate: false,\n });\n\n i18n.init();\n if (!i18n.isInitialized) {\n throw new Error('i18next was unexpectedly not initialized');\n }\n\n const { language: initialLanguage } = options.languageApi.getLanguage();\n if (initialLanguage !== DEFAULT_LANGUAGE) {\n i18n.changeLanguage(initialLanguage);\n }\n\n const loader = new ResourceLoader(loaded => {\n i18n.addResourceBundle(\n loaded.language,\n loaded.namespace,\n removeNulls(loaded.messages),\n false, // do not merge with existing translations\n true, // overwrite translations\n );\n });\n\n const resources = options?.resources || [];\n // Iterate in reverse, giving higher priority to resources registered later\n for (let i = resources.length - 1; i >= 0; i--) {\n const resource = resources[i];\n if (resource.$$type === '@backstage/TranslationResource') {\n loader.addTranslationResource(resource);\n } else if (resource.$$type === '@backstage/TranslationMessages') {\n // Overrides for default messages, created with createTranslationMessages and installed via app\n i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n resource.id,\n removeNulls(resource.messages),\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n }\n }\n\n const instance = new I18nextTranslationApi(\n i18n,\n loader,\n options.languageApi.getLanguage().language,\n );\n\n options.languageApi.language$().subscribe(({ language }) => {\n instance.#changeLanguage(language);\n });\n\n return instance;\n }\n\n #i18n: I18n;\n #loader: ResourceLoader;\n #language: string;\n\n /** Keep track of which refs we have registered default resources for */\n #registeredRefs = new Set<string>();\n /** Notify observers when language changes */\n #languageChangeListeners = new Set<() => void>();\n\n private constructor(i18n: I18n, loader: ResourceLoader, language: string) {\n this.#i18n = i18n;\n this.#loader = loader;\n this.#language = language;\n }\n\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return this.#createSnapshot(internalRef);\n }\n\n translation$<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): Observable<TranslationSnapshot<TMessages>> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return new ObservableImpl<TranslationSnapshot<TMessages>>(subscriber => {\n let loadTicket = {}; // To check for stale loads\n\n const loadResource = () => {\n loadTicket = {};\n const ticket = loadTicket;\n this.#loader.load(this.#language, internalRef.id).then(\n () => {\n if (ticket === loadTicket) {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n }\n }\n },\n error => {\n if (ticket === loadTicket) {\n subscriber.error(Array.isArray(error) ? error[0] : error);\n }\n },\n );\n };\n\n const onChange = () => {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n } else {\n loadResource();\n }\n };\n\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n loadResource();\n }\n\n this.#languageChangeListeners.add(onChange);\n return () => {\n this.#languageChangeListeners.delete(onChange);\n };\n });\n }\n\n #changeLanguage(language: string): void {\n if (this.#language !== language) {\n this.#language = language;\n this.#i18n.changeLanguage(language);\n this.#languageChangeListeners.forEach(listener => listener());\n }\n }\n\n #createSnapshot<TMessages extends { [key in string]: string }>(\n internalRef: InternalTranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n return { ready: false };\n }\n\n const t = this.#i18n.getFixedT(\n null,\n internalRef.id,\n ) as TranslationFunction<TMessages>;\n\n return {\n ready: true,\n t,\n };\n }\n\n #registerDefaults(internalRef: InternalTranslationRef): void {\n if (this.#registeredRefs.has(internalRef.id)) {\n return;\n }\n this.#registeredRefs.add(internalRef.id);\n\n const defaultMessages = internalRef.getDefaultMessages();\n this.#i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n internalRef.id,\n defaultMessages,\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n\n const defaultResource = internalRef.getDefaultResource();\n if (defaultResource) {\n this.#loader.addTranslationResource(defaultResource);\n }\n }\n}\n"],"names":["createI18n"],"mappings":"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"}
@@ -1,60 +0,0 @@
1
- import React, { useMemo, useState, useEffect } from 'react';
2
- import { useApi, appThemeApiRef } from '@backstage/core-plugin-api';
3
- import useObservable from 'react-use/esm/useObservable';
4
-
5
- function resolveTheme(themeId, shouldPreferDark, themes) {
6
- if (themeId !== void 0) {
7
- const selectedTheme = themes.find((theme) => theme.id === themeId);
8
- if (selectedTheme) {
9
- return selectedTheme;
10
- }
11
- }
12
- if (shouldPreferDark) {
13
- const darkTheme = themes.find((theme) => theme.variant === "dark");
14
- if (darkTheme) {
15
- return darkTheme;
16
- }
17
- }
18
- const lightTheme = themes.find((theme) => theme.variant === "light");
19
- if (lightTheme) {
20
- return lightTheme;
21
- }
22
- return themes[0];
23
- }
24
- const useShouldPreferDarkTheme = () => {
25
- const mediaQuery = useMemo(
26
- () => window.matchMedia("(prefers-color-scheme: dark)"),
27
- []
28
- );
29
- const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches);
30
- useEffect(() => {
31
- const listener = (event) => {
32
- setPrefersDark(event.matches);
33
- };
34
- mediaQuery.addListener(listener);
35
- return () => {
36
- mediaQuery.removeListener(listener);
37
- };
38
- }, [mediaQuery]);
39
- return shouldPreferDark;
40
- };
41
- function AppThemeProvider({ children }) {
42
- const appThemeApi = useApi(appThemeApiRef);
43
- const themeId = useObservable(
44
- appThemeApi.activeThemeId$(),
45
- appThemeApi.getActiveThemeId()
46
- );
47
- const shouldPreferDark = Boolean(window.matchMedia) ? useShouldPreferDarkTheme() : false;
48
- const appTheme = resolveTheme(
49
- themeId,
50
- shouldPreferDark,
51
- appThemeApi.getInstalledThemes()
52
- );
53
- if (!appTheme) {
54
- throw new Error("App has no themes");
55
- }
56
- return /* @__PURE__ */ React.createElement(appTheme.Provider, { children });
57
- }
58
-
59
- export { AppThemeProvider };
60
- //# sourceMappingURL=AppThemeProvider.esm.js.map