@gooddata/create-pluggable-module 11.40.0-alpha.3

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 (93) hide show
  1. package/README.md +59 -0
  2. package/dist/templates/harness/.env.template +9 -0
  3. package/dist/templates/harness/.gitignore +10 -0
  4. package/dist/templates/harness/README.md +45 -0
  5. package/dist/templates/harness/eslint.config.ts +3 -0
  6. package/dist/templates/harness/index.html +13 -0
  7. package/dist/templates/harness/package.json +70 -0
  8. package/dist/templates/harness/src/app.tsx +81 -0
  9. package/dist/templates/harness/tsconfig.json +70 -0
  10. package/dist/templates/harness/typings.d.ts +13 -0
  11. package/dist/templates/harness/vite.config.ts +111 -0
  12. package/dist/templates/module/.gitignore +11 -0
  13. package/dist/templates/module/README.md +35 -0
  14. package/dist/templates/module/eslint.config.ts +3 -0
  15. package/dist/templates/module/package.json +84 -0
  16. package/dist/templates/module/src/App.test.tsx +53 -0
  17. package/dist/templates/module/src/App.tsx +34 -0
  18. package/dist/templates/module/src/AppProviders.tsx +26 -0
  19. package/dist/templates/module/src/index.ts +3 -0
  20. package/dist/templates/module/src/pluggableApp.tsx +56 -0
  21. package/dist/templates/module/src/translations/de-DE.json +6 -0
  22. package/dist/templates/module/src/translations/en-AU.json +6 -0
  23. package/dist/templates/module/src/translations/en-GB.json +6 -0
  24. package/dist/templates/module/src/translations/en-US.json +6 -0
  25. package/dist/templates/module/src/translations/es-419.json +6 -0
  26. package/dist/templates/module/src/translations/es-ES.json +6 -0
  27. package/dist/templates/module/src/translations/fi-FI.json +6 -0
  28. package/dist/templates/module/src/translations/fr-CA.json +6 -0
  29. package/dist/templates/module/src/translations/fr-FR.json +6 -0
  30. package/dist/templates/module/src/translations/id-ID.json +6 -0
  31. package/dist/templates/module/src/translations/it-IT.json +6 -0
  32. package/dist/templates/module/src/translations/ja-JP.json +6 -0
  33. package/dist/templates/module/src/translations/ko-KR.json +6 -0
  34. package/dist/templates/module/src/translations/nl-NL.json +6 -0
  35. package/dist/templates/module/src/translations/pl-PL.json +6 -0
  36. package/dist/templates/module/src/translations/pt-BR.json +6 -0
  37. package/dist/templates/module/src/translations/pt-PT.json +6 -0
  38. package/dist/templates/module/src/translations/ru-RU.json +6 -0
  39. package/dist/templates/module/src/translations/sl-SI.json +6 -0
  40. package/dist/templates/module/src/translations/th-TH.json +6 -0
  41. package/dist/templates/module/src/translations/tr-TR.json +6 -0
  42. package/dist/templates/module/src/translations/translations.ts +71 -0
  43. package/dist/templates/module/src/translations/uk-UA.json +6 -0
  44. package/dist/templates/module/src/translations/vi-VN.json +6 -0
  45. package/dist/templates/module/src/translations/zh-HK.json +6 -0
  46. package/dist/templates/module/src/translations/zh-Hans.json +6 -0
  47. package/dist/templates/module/src/translations/zh-Hant.json +6 -0
  48. package/dist/templates/module/tsconfig.json +65 -0
  49. package/dist/templates/module/vite.config.ts +140 -0
  50. package/dist/templates/module/vitest.config.ts +12 -0
  51. package/esm/engine/__tests__/anchors.test.d.ts +1 -0
  52. package/esm/engine/__tests__/anchors.test.js +44 -0
  53. package/esm/engine/__tests__/copyTemplate.test.d.ts +1 -0
  54. package/esm/engine/__tests__/copyTemplate.test.js +59 -0
  55. package/esm/engine/__tests__/derived.test.d.ts +1 -0
  56. package/esm/engine/__tests__/derived.test.js +30 -0
  57. package/esm/engine/__tests__/jsonAnchors.test.d.ts +1 -0
  58. package/esm/engine/__tests__/jsonAnchors.test.js +52 -0
  59. package/esm/engine/__tests__/repoDetection.test.d.ts +1 -0
  60. package/esm/engine/__tests__/repoDetection.test.js +63 -0
  61. package/esm/engine/__tests__/scrubPackageJsonVersions.d.ts +1 -0
  62. package/esm/engine/__tests__/scrubPackageJsonVersions.js +34 -0
  63. package/esm/engine/__tests__/scrubPackageJsonVersions.test.d.ts +1 -0
  64. package/esm/engine/__tests__/scrubPackageJsonVersions.test.js +79 -0
  65. package/esm/engine/__tests__/snapshot.test.d.ts +1 -0
  66. package/esm/engine/__tests__/snapshot.test.js +93 -0
  67. package/esm/engine/anchors.d.ts +17 -0
  68. package/esm/engine/anchors.js +43 -0
  69. package/esm/engine/checkAnchors.d.ts +12 -0
  70. package/esm/engine/checkAnchors.js +44 -0
  71. package/esm/engine/copyTemplate.d.ts +11 -0
  72. package/esm/engine/copyTemplate.js +70 -0
  73. package/esm/engine/derived.d.ts +32 -0
  74. package/esm/engine/derived.js +85 -0
  75. package/esm/engine/jsonAnchors.d.ts +13 -0
  76. package/esm/engine/jsonAnchors.js +42 -0
  77. package/esm/engine/prompts.d.ts +15 -0
  78. package/esm/engine/prompts.js +157 -0
  79. package/esm/engine/repoDetection.d.ts +18 -0
  80. package/esm/engine/repoDetection.js +47 -0
  81. package/esm/engine/runProfile.d.ts +18 -0
  82. package/esm/engine/runProfile.js +179 -0
  83. package/esm/engine/spawn.d.ts +13 -0
  84. package/esm/engine/spawn.js +30 -0
  85. package/esm/index.d.ts +10 -0
  86. package/esm/index.js +62 -0
  87. package/esm/profiles/__tests__/client.test.d.ts +1 -0
  88. package/esm/profiles/__tests__/client.test.js +151 -0
  89. package/esm/profiles/client.d.ts +2 -0
  90. package/esm/profiles/client.js +208 -0
  91. package/esm/types.d.ts +194 -0
  92. package/esm/types.js +2 -0
  93. package/package.json +77 -0
@@ -0,0 +1,53 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ import { render, screen } from "@testing-library/react";
4
+ import { IntlProvider } from "react-intl";
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ import { BackendProvider } from "@gooddata/sdk-ui";
8
+ import { PlatformContextProvider, type IClientPlatformContext } from "@gooddata/sdk-ui-pluggable-application";
9
+
10
+ import { App } from "./App.js";
11
+
12
+ function createMockCtx(): IClientPlatformContext {
13
+ return {
14
+ version: "1.0",
15
+ auth: { type: "contextDeferred" },
16
+ user: { login: "test@example.com" },
17
+ organization: { id: "org-1", title: "Test Org" },
18
+ userSettings: {} as IClientPlatformContext["userSettings"],
19
+ whiteLabeling: undefined,
20
+ embeddingMode: "none",
21
+ backend: {
22
+ // Minimal mock — extend as needed for your tests
23
+ } as IClientPlatformContext["backend"],
24
+ } as IClientPlatformContext;
25
+ }
26
+
27
+ function renderWithProviders(ui: React.ReactElement) {
28
+ const ctx = createMockCtx();
29
+ return render(
30
+ <PlatformContextProvider value={ctx}>
31
+ <BackendProvider backend={ctx.backend}>
32
+ <IntlProvider
33
+ locale="en-US"
34
+ messages={{ "gdc-app-template-name.title": "{applicationTemplateTitle}" }}
35
+ >
36
+ {ui}
37
+ </IntlProvider>
38
+ </BackendProvider>
39
+ </PlatformContextProvider>,
40
+ );
41
+ }
42
+
43
+ describe("App", () => {
44
+ it("renders the application title", () => {
45
+ renderWithProviders(<App />);
46
+ expect(screen.getByText("{applicationTemplateTitle}")).toBeTruthy();
47
+ });
48
+
49
+ it("renders user information from context", () => {
50
+ renderWithProviders(<App />);
51
+ expect(screen.getByText(/test@example.com/)).toBeTruthy();
52
+ });
53
+ });
@@ -0,0 +1,34 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ import { FormattedMessage } from "react-intl";
4
+
5
+ import {
6
+ type IPluggableAppEvent,
7
+ type IPluggableAppTelemetryCallbacks,
8
+ } from "@gooddata/sdk-pluggable-application-model";
9
+ import { useWorkspace } from "@gooddata/sdk-ui";
10
+ import { usePlatformContextStrict } from "@gooddata/sdk-ui-pluggable-application";
11
+
12
+ interface IAppProps {
13
+ onEvent?: (e: IPluggableAppEvent) => void;
14
+ onTelemetryEvent?: IPluggableAppTelemetryCallbacks;
15
+ }
16
+
17
+ export function App({ onEvent: _onEvent, onTelemetryEvent: _onTelemetryEvent }: IAppProps) {
18
+ const ctx = usePlatformContextStrict();
19
+ const workspaceId = useWorkspace();
20
+
21
+ return (
22
+ <div>
23
+ <h1>
24
+ <FormattedMessage id="gdc-app-template-name.title" />
25
+ </h1>
26
+ <p>Your pluggable application is running.</p>
27
+ <ul>
28
+ <li>User: {ctx.user.login}</li>
29
+ <li>Organization: {ctx.organization?.title ?? ctx.organization?.id ?? "n/a"}</li>
30
+ {workspaceId ? <li>Workspace: {workspaceId}</li> : null}
31
+ </ul>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,26 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ import { type PropsWithChildren } from "react";
4
+
5
+ import { type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
6
+ import { AppProviders as SdkAppProviders } from "@gooddata/sdk-ui-pluggable-application";
7
+
8
+ import { DEFAULT_LANGUAGE, DEFAULT_MESSAGES, resolveMessages } from "./translations/translations.js";
9
+
10
+ interface IAppProvidersProps {
11
+ ctx: IPlatformContext;
12
+ }
13
+
14
+ export function AppProviders({ ctx, children }: PropsWithChildren<IAppProvidersProps>) {
15
+ return (
16
+ <SdkAppProviders
17
+ ctx={ctx}
18
+ packageName="gdc-app-template-name-module"
19
+ resolveMessages={resolveMessages}
20
+ defaultMessages={DEFAULT_MESSAGES}
21
+ defaultLanguage={DEFAULT_LANGUAGE}
22
+ >
23
+ {children}
24
+ </SdkAppProviders>
25
+ );
26
+ }
@@ -0,0 +1,3 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ export { pluggableApp } from "./pluggableApp.js";
@@ -0,0 +1,56 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ import { createRoot, type Root } from "react-dom/client";
4
+
5
+ import {
6
+ type IPluggableApp,
7
+ type IPluggableApplicationMountHandle,
8
+ type IPluggableApplicationMountOptions,
9
+ type IPluggableAppEvent,
10
+ type IPluggableAppTelemetryCallbacks,
11
+ type IPlatformContext,
12
+ } from "@gooddata/sdk-pluggable-application-model";
13
+
14
+ import { App } from "./App.js";
15
+ import { AppProviders } from "./AppProviders.js";
16
+
17
+ function renderApp(
18
+ root: Root,
19
+ ctx: IPlatformContext,
20
+ _basePath: string,
21
+ onEvent: ((e: IPluggableAppEvent) => void) | undefined,
22
+ onTelemetryEvent: IPluggableAppTelemetryCallbacks | undefined,
23
+ ) {
24
+ root.render(
25
+ <AppProviders ctx={ctx}>
26
+ <App onEvent={onEvent} onTelemetryEvent={onTelemetryEvent} />
27
+ </AppProviders>,
28
+ );
29
+ }
30
+
31
+ export const pluggableApp: IPluggableApp = {
32
+ mount({
33
+ container,
34
+ ctx,
35
+ basePath,
36
+ onEvent,
37
+ onTelemetryEvent,
38
+ }: IPluggableApplicationMountOptions): IPluggableApplicationMountHandle {
39
+ // To customize the host header (e.g. add app-specific help menu items),
40
+ // destructure `onHeaderChange` from the mount options and call:
41
+ // onHeaderChange?.({ helpMenuItems: [...] });
42
+ // See modules/gdc-catalog/module/src/pluggableApp.tsx for a working example.
43
+ const root = createRoot(container);
44
+
45
+ renderApp(root, ctx, basePath, onEvent, onTelemetryEvent);
46
+
47
+ return {
48
+ unmount() {
49
+ root.unmount();
50
+ },
51
+ updateContext(nextCtx: IPlatformContext) {
52
+ renderApp(root, nextCtx, basePath, onEvent, onTelemetryEvent);
53
+ },
54
+ };
55
+ },
56
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,71 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ import { memoize } from "lodash-es";
4
+
5
+ import {
6
+ type ILocale,
7
+ type ITranslations,
8
+ DEFAULT_MESSAGES as SDK_DEFAULT_MESSAGES,
9
+ resolveMessages as resolveSdkMessages,
10
+ } from "@gooddata/sdk-ui";
11
+ import { removeMetadata } from "@gooddata/util";
12
+
13
+ import en_US from "./en-US.json" with { type: "json" };
14
+
15
+ export const DEFAULT_LANGUAGE: ILocale = "en-US";
16
+ export const DEFAULT_MESSAGES: Record<string, ITranslations> = {
17
+ [DEFAULT_LANGUAGE]: {
18
+ ...SDK_DEFAULT_MESSAGES[DEFAULT_LANGUAGE],
19
+ ...removeMetadata(en_US),
20
+ },
21
+ };
22
+
23
+ const loadLocale = (importFn: () => Promise<{ default: unknown }>): (() => Promise<ITranslations>) => {
24
+ return () => importFn().then((module) => removeMetadata(module.default || module));
25
+ };
26
+
27
+ type LocaleOptions = Record<ILocale, () => Promise<ITranslations>>;
28
+
29
+ const asyncComponentTranslations: LocaleOptions = {
30
+ "en-US": () => Promise.resolve(removeMetadata(en_US)),
31
+ "en-US-x-24h": () => Promise.resolve(removeMetadata(en_US)),
32
+ "de-DE": loadLocale(() => import("./de-DE.json")),
33
+ "en-AU": loadLocale(() => import("./en-AU.json")),
34
+ "en-GB": loadLocale(() => import("./en-GB.json")),
35
+ "es-419": loadLocale(() => import("./es-419.json")),
36
+ "es-ES": loadLocale(() => import("./es-ES.json")),
37
+ "fi-FI": loadLocale(() => import("./fi-FI.json")),
38
+ "fr-CA": loadLocale(() => import("./fr-CA.json")),
39
+ "fr-FR": loadLocale(() => import("./fr-FR.json")),
40
+ "id-ID": loadLocale(() => import("./id-ID.json")),
41
+ "it-IT": loadLocale(() => import("./it-IT.json")),
42
+ "ja-JP": loadLocale(() => import("./ja-JP.json")),
43
+ "ko-KR": loadLocale(() => import("./ko-KR.json")),
44
+ "nl-NL": loadLocale(() => import("./nl-NL.json")),
45
+ "pl-PL": loadLocale(() => import("./pl-PL.json")),
46
+ "pt-BR": loadLocale(() => import("./pt-BR.json")),
47
+ "pt-PT": loadLocale(() => import("./pt-PT.json")),
48
+ "ru-RU": loadLocale(() => import("./ru-RU.json")),
49
+ "sl-SI": loadLocale(() => import("./sl-SI.json")),
50
+ "th-TH": loadLocale(() => import("./th-TH.json")),
51
+ "tr-TR": loadLocale(() => import("./tr-TR.json")),
52
+ "vi-VN": loadLocale(() => import("./vi-VN.json")),
53
+ "zh-Hans": loadLocale(() => import("./zh-Hans.json")),
54
+ "zh-Hant": loadLocale(() => import("./zh-Hant.json")),
55
+ "uk-UA": loadLocale(() => import("./uk-UA.json")),
56
+ "zh-HK": loadLocale(() => import("./zh-HK.json")),
57
+ };
58
+
59
+ const resolveMessagesInternal = async (locale: string): Promise<ITranslations> => {
60
+ const componentLoader =
61
+ asyncComponentTranslations[locale as ILocale] || asyncComponentTranslations[DEFAULT_LANGUAGE];
62
+
63
+ const [componentMessages, sdkMessages] = await Promise.all([
64
+ componentLoader(),
65
+ resolveSdkMessages(locale),
66
+ ]);
67
+
68
+ return { ...sdkMessages, ...componentMessages };
69
+ };
70
+
71
+ export const resolveMessages: (locale: string) => Promise<ITranslations> = memoize(resolveMessagesInternal);
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "gdc-app-template-name.title": {
3
+ "text": "{applicationTemplateTitle}",
4
+ "crowdinContext": "Application title displayed in the navigation menu"
5
+ }
6
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "noFallthroughCasesInSwitch": true,
5
+ "noImplicitOverride": true,
6
+ "noPropertyAccessFromIndexSignature": true,
7
+ "exactOptionalPropertyTypes": false,
8
+ "useUnknownInCatchVariables": true,
9
+ "noUncheckedIndexedAccess": false,
10
+ "noUnusedLocals": true,
11
+ "noUnusedParameters": true,
12
+ "allowUnreachableCode": false,
13
+ "allowUnusedLabels": false,
14
+ "noImplicitReturns": true,
15
+ "alwaysStrict": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "lib": [
18
+ "es2022",
19
+ "dom",
20
+ "dom.iterable"
21
+ ],
22
+ "target": "es2022",
23
+ "skipLibCheck": true,
24
+ "esModuleInterop": true,
25
+ "module": "nodenext",
26
+ "moduleResolution": "nodenext",
27
+ "importHelpers": true,
28
+ "incremental": false,
29
+ "resolveJsonModule": true,
30
+ "declaration": true,
31
+ "jsx": "react-jsx",
32
+ "allowJs": true,
33
+ "noEmit": true,
34
+ "outDir": "./dist",
35
+ "moduleDetection": "force",
36
+ "allowSyntheticDefaultImports": true,
37
+ "resolvePackageJsonExports": true,
38
+ "resolvePackageJsonImports": true,
39
+ "useDefineForClassFields": true,
40
+ "noImplicitAny": true,
41
+ "noImplicitThis": true,
42
+ "strictNullChecks": true,
43
+ "strictFunctionTypes": true,
44
+ "strictBindCallApply": true,
45
+ "strictPropertyInitialization": true,
46
+ "strictBuiltinIteratorReturn": true
47
+ },
48
+ "files": [
49
+ "./eslint.config.ts",
50
+ "./vite.config.ts",
51
+ "./vitest.config.ts",
52
+ "./src/App.test.tsx",
53
+ "./src/App.tsx",
54
+ "./src/AppProviders.tsx",
55
+ "./src/index.ts",
56
+ "./src/pluggableApp.tsx",
57
+ "./src/translations/translations.ts"
58
+ ],
59
+ "exclude": [
60
+ "**/node_modules",
61
+ "**/esm",
62
+ "**/temp",
63
+ "**/dist"
64
+ ]
65
+ }
@@ -0,0 +1,140 @@
1
+ // (C) 2026 GoodData Corporation
2
+
3
+ //
4
+ // Pluggable-only build: produces the Module Federation artifacts
5
+ // (remoteEntry.js + pluggableApp chunks) needed by the host app.
6
+ //
7
+ // One artifact, two publish targets: with `base: "./"` the same dist/
8
+ // works whether served from host nginx (/organization/remotes/<app>/)
9
+ // or from S3 (https://<bucket>/<prefix>/<app>/).
10
+ //
11
+ // Local dev (module federation):
12
+ // `vite build --watch` (terminal 1)
13
+ // `vite preview` (terminal 2)
14
+ // Note: day-to-day development is faster via the sibling harness/,
15
+ // which imports the module source directly without federation.
16
+ //
17
+ // Build:
18
+ // `vite build` → dist/
19
+ //
20
+
21
+ import { execSync } from "child_process";
22
+ import { readFileSync } from "fs";
23
+ import { join, resolve } from "path";
24
+
25
+ import { federation } from "@module-federation/vite";
26
+ import react from "@vitejs/plugin-react";
27
+ import { type UserConfig, defaultClientConditions, defineConfig, loadEnv } from "vite";
28
+
29
+ function tryExecSync(command: string, fallback: string): string {
30
+ try {
31
+ return execSync(command, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim() || fallback;
32
+ } catch {
33
+ return fallback;
34
+ }
35
+ }
36
+
37
+ function gitRevisionPlugin() {
38
+ const commitHash = tryExecSync("git rev-parse HEAD --quiet", process.env["GIT_COMMIT"] ?? "dev");
39
+
40
+ return {
41
+ name: "git-revision",
42
+ config() {
43
+ return {
44
+ define: {
45
+ "window.COMMITHASH": JSON.stringify(commitHash),
46
+ },
47
+ };
48
+ },
49
+ };
50
+ }
51
+
52
+ const certDir = join(process.env["HOME"] || process.env["USERPROFILE"] || "", ".gooddata", "certs");
53
+ let httpsConfig: import("vite").ServerOptions["https"] | undefined;
54
+
55
+ try {
56
+ httpsConfig = {
57
+ ca: readFileSync(join(certDir, "rootCA.pem")),
58
+ key: readFileSync(join(certDir, "localhost-key.pem")),
59
+ cert: readFileSync(join(certDir, "localhost-cert.pem")),
60
+ };
61
+ // oxlint-disable-next-line @typescript-eslint/no-unused-vars
62
+ } catch (e) {
63
+ // eslint-disable-next-line no-console
64
+ console.info(`No certificates found in ${certDir}, skipping HTTPS configuration`);
65
+ httpsConfig = undefined;
66
+ }
67
+
68
+ // eslint-disable-next-line no-restricted-exports
69
+ export default defineConfig(({ mode, command }): UserConfig => {
70
+ const env = loadEnv(mode, process.cwd(), "");
71
+ const port = parseInt(env["PORT"] || "{modulePort}");
72
+ const isDev = command === "serve"; // dev server only
73
+
74
+ return {
75
+ root: ".",
76
+ // Relative base so asset URLs resolve via import.meta.url at runtime,
77
+ // allowing the remote to be served from any path (nginx, S3, CDN)
78
+ // without coupling to a specific host URL.
79
+ base: "./",
80
+ publicDir: false,
81
+ resolve: {
82
+ // resolve.conditions REPLACES Vite's defaults rather than extending them
83
+ // (Vite 8's mergeWithDefaultsRecursively assigns arrays directly), so we must
84
+ // spread defaultClientConditions to keep "browser" / "module" / dev-prod
85
+ // resolution. Without "browser", packages like react-textarea-autosize fall
86
+ // through to their node-targeted default export (a stub that ignores
87
+ // minRows/maxRows).
88
+ // `source` is added only in dev so production builds use the published
89
+ // artifacts of workspace deps, not their TS source.
90
+ conditions: [...defaultClientConditions, ...(isDev ? ["source"] : [])],
91
+ dedupe: [
92
+ "@gooddata/sdk-ui",
93
+ "@gooddata/sdk-ui-kit",
94
+ "@gooddata/sdk-ui-ext",
95
+ "react",
96
+ "react-dom",
97
+ ],
98
+ },
99
+ define: {
100
+ REACT_APP_SDK_BACKEND: JSON.stringify("TIGER"),
101
+ PRODUCTION: JSON.stringify(mode === "production"),
102
+ "process.env": JSON.stringify({}),
103
+ },
104
+ // `vite preview` serves dist/ for local dev against the host app.
105
+ preview: {
106
+ port,
107
+ https: httpsConfig,
108
+ cors: true,
109
+ },
110
+ build: {
111
+ outDir: "dist",
112
+ sourcemap: mode !== "production",
113
+ target: "es2022",
114
+ chunkSizeWarningLimit: 10000,
115
+ rolldownOptions: {
116
+ input: resolve(__dirname, "src/pluggableApp.tsx"),
117
+ output: {
118
+ entryFileNames: "static/js/[name].[hash].js",
119
+ chunkFileNames: "static/js/[name].[hash].chunk.js",
120
+ assetFileNames: "static/[hash][extname]",
121
+ },
122
+ },
123
+ },
124
+ plugins: [
125
+ federation({
126
+ name: "{applicationTemplateFederationName}",
127
+ filename: "remoteEntry.js",
128
+ dts: false,
129
+ exposes: {
130
+ "./pluggableApp": "./src/pluggableApp.tsx",
131
+ },
132
+ // Use idle-based timeout instead of the default 10s absolute deadline,
133
+ // which flakes on loaded CI runners when the transform is slow but progressing.
134
+ moduleParseIdleTimeout: 60,
135
+ }),
136
+ react(),
137
+ gitRevisionPlugin(),
138
+ ],
139
+ };
140
+ });