@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.
- package/README.md +59 -0
- package/dist/templates/harness/.env.template +9 -0
- package/dist/templates/harness/.gitignore +10 -0
- package/dist/templates/harness/README.md +45 -0
- package/dist/templates/harness/eslint.config.ts +3 -0
- package/dist/templates/harness/index.html +13 -0
- package/dist/templates/harness/package.json +70 -0
- package/dist/templates/harness/src/app.tsx +81 -0
- package/dist/templates/harness/tsconfig.json +70 -0
- package/dist/templates/harness/typings.d.ts +13 -0
- package/dist/templates/harness/vite.config.ts +111 -0
- package/dist/templates/module/.gitignore +11 -0
- package/dist/templates/module/README.md +35 -0
- package/dist/templates/module/eslint.config.ts +3 -0
- package/dist/templates/module/package.json +84 -0
- package/dist/templates/module/src/App.test.tsx +53 -0
- package/dist/templates/module/src/App.tsx +34 -0
- package/dist/templates/module/src/AppProviders.tsx +26 -0
- package/dist/templates/module/src/index.ts +3 -0
- package/dist/templates/module/src/pluggableApp.tsx +56 -0
- package/dist/templates/module/src/translations/de-DE.json +6 -0
- package/dist/templates/module/src/translations/en-AU.json +6 -0
- package/dist/templates/module/src/translations/en-GB.json +6 -0
- package/dist/templates/module/src/translations/en-US.json +6 -0
- package/dist/templates/module/src/translations/es-419.json +6 -0
- package/dist/templates/module/src/translations/es-ES.json +6 -0
- package/dist/templates/module/src/translations/fi-FI.json +6 -0
- package/dist/templates/module/src/translations/fr-CA.json +6 -0
- package/dist/templates/module/src/translations/fr-FR.json +6 -0
- package/dist/templates/module/src/translations/id-ID.json +6 -0
- package/dist/templates/module/src/translations/it-IT.json +6 -0
- package/dist/templates/module/src/translations/ja-JP.json +6 -0
- package/dist/templates/module/src/translations/ko-KR.json +6 -0
- package/dist/templates/module/src/translations/nl-NL.json +6 -0
- package/dist/templates/module/src/translations/pl-PL.json +6 -0
- package/dist/templates/module/src/translations/pt-BR.json +6 -0
- package/dist/templates/module/src/translations/pt-PT.json +6 -0
- package/dist/templates/module/src/translations/ru-RU.json +6 -0
- package/dist/templates/module/src/translations/sl-SI.json +6 -0
- package/dist/templates/module/src/translations/th-TH.json +6 -0
- package/dist/templates/module/src/translations/tr-TR.json +6 -0
- package/dist/templates/module/src/translations/translations.ts +71 -0
- package/dist/templates/module/src/translations/uk-UA.json +6 -0
- package/dist/templates/module/src/translations/vi-VN.json +6 -0
- package/dist/templates/module/src/translations/zh-HK.json +6 -0
- package/dist/templates/module/src/translations/zh-Hans.json +6 -0
- package/dist/templates/module/src/translations/zh-Hant.json +6 -0
- package/dist/templates/module/tsconfig.json +65 -0
- package/dist/templates/module/vite.config.ts +140 -0
- package/dist/templates/module/vitest.config.ts +12 -0
- package/esm/engine/__tests__/anchors.test.d.ts +1 -0
- package/esm/engine/__tests__/anchors.test.js +44 -0
- package/esm/engine/__tests__/copyTemplate.test.d.ts +1 -0
- package/esm/engine/__tests__/copyTemplate.test.js +59 -0
- package/esm/engine/__tests__/derived.test.d.ts +1 -0
- package/esm/engine/__tests__/derived.test.js +30 -0
- package/esm/engine/__tests__/jsonAnchors.test.d.ts +1 -0
- package/esm/engine/__tests__/jsonAnchors.test.js +52 -0
- package/esm/engine/__tests__/repoDetection.test.d.ts +1 -0
- package/esm/engine/__tests__/repoDetection.test.js +63 -0
- package/esm/engine/__tests__/scrubPackageJsonVersions.d.ts +1 -0
- package/esm/engine/__tests__/scrubPackageJsonVersions.js +34 -0
- package/esm/engine/__tests__/scrubPackageJsonVersions.test.d.ts +1 -0
- package/esm/engine/__tests__/scrubPackageJsonVersions.test.js +79 -0
- package/esm/engine/__tests__/snapshot.test.d.ts +1 -0
- package/esm/engine/__tests__/snapshot.test.js +93 -0
- package/esm/engine/anchors.d.ts +17 -0
- package/esm/engine/anchors.js +43 -0
- package/esm/engine/checkAnchors.d.ts +12 -0
- package/esm/engine/checkAnchors.js +44 -0
- package/esm/engine/copyTemplate.d.ts +11 -0
- package/esm/engine/copyTemplate.js +70 -0
- package/esm/engine/derived.d.ts +32 -0
- package/esm/engine/derived.js +85 -0
- package/esm/engine/jsonAnchors.d.ts +13 -0
- package/esm/engine/jsonAnchors.js +42 -0
- package/esm/engine/prompts.d.ts +15 -0
- package/esm/engine/prompts.js +157 -0
- package/esm/engine/repoDetection.d.ts +18 -0
- package/esm/engine/repoDetection.js +47 -0
- package/esm/engine/runProfile.d.ts +18 -0
- package/esm/engine/runProfile.js +179 -0
- package/esm/engine/spawn.d.ts +13 -0
- package/esm/engine/spawn.js +30 -0
- package/esm/index.d.ts +10 -0
- package/esm/index.js +62 -0
- package/esm/profiles/__tests__/client.test.d.ts +1 -0
- package/esm/profiles/__tests__/client.test.js +151 -0
- package/esm/profiles/client.d.ts +2 -0
- package/esm/profiles/client.js +208 -0
- package/esm/types.d.ts +194 -0
- package/esm/types.js +2 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @gooddata/create-pluggable-module
|
|
2
|
+
|
|
3
|
+
Scaffolder for GoodData pluggable applications. Generates a `module/` (the federated app) and `harness/` (a thin host for running it standalone) under your repo's `modules/` directory, with all the wiring required to build, run, and federate the result.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm init @gooddata/pluggable-module
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`npm init` resolves this package and runs its CLI. You'll be prompted for:
|
|
12
|
+
|
|
13
|
+
- **App name** — must start with `gdc-` (e.g. `gdc-revenue-tracker`)
|
|
14
|
+
- **Title** — the en-US display name shown in the host menu
|
|
15
|
+
- **Scope** — `workspace` (per-workspace app) or `organization` (org-wide app)
|
|
16
|
+
- **Destination path** _(optional)_ — defaults to `./modules/<app-name>/`
|
|
17
|
+
|
|
18
|
+
The scaffolder detects your package manager from the repo's lockfile (`npm`, `yarn`, `pnpm`) and runs `install` once the files are in place. If a `rush.json` is found, it also registers the new packages in it so `rush update` picks them up.
|
|
19
|
+
|
|
20
|
+
## What you get
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
modules/<app-name>/
|
|
24
|
+
├── module/ # The federated pluggable app — Module Federation remote, built with Vite.
|
|
25
|
+
└── harness/ # Standalone host that imports the module directly for fast local dev.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Each side ships its own `package.json`, `tsconfig.json`, `vite.config.ts`, eslint config, and starter source files. SDK dependencies are stamped at the matching `@gooddata/sdk-*` release version (see _Version coupling_ below).
|
|
29
|
+
|
|
30
|
+
Once scaffolded:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
cd modules/<app-name>/harness
|
|
34
|
+
cp .env.template .env # then fill in TIGER_API_TOKEN
|
|
35
|
+
npm run dev # fast local dev loop — harness imports module source directly
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To run the module as a federation remote against the harness (the production-shape dev loop):
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
cd modules/<app-name>/module
|
|
42
|
+
npm run dev # serves remoteEntry.js at https://localhost:<modulePort>/
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Version coupling
|
|
46
|
+
|
|
47
|
+
This package ships on the same release train as the rest of the GoodData SDK (`versionPolicyName: "sdk"`). Scaffolded `module/package.json` and `harness/package.json` get their `@gooddata/sdk-*` deps stamped with this scaffolder's version, so the templates always align with the SDK release they were generated against. Upgrading a scaffolded app means bumping all `@gooddata/sdk-*` deps in lockstep.
|
|
48
|
+
|
|
49
|
+
## Programmatic API
|
|
50
|
+
|
|
51
|
+
The CLI is a thin wrapper over an exported engine. Consumers building their own scaffolders can import the engine directly:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { runProfile, type IScaffoldProfile } from "@gooddata/create-pluggable-module";
|
|
55
|
+
|
|
56
|
+
await runProfile(myCustomProfile);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
See `src/types.ts` for the `IScaffoldProfile` contract.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
BACKEND_URL=
|
|
2
|
+
TIGER_API_TOKEN=
|
|
3
|
+
|
|
4
|
+
# Optional: URL of this module's Module Federation remote entry for local development.
|
|
5
|
+
# When set, the harness loads the pluggable app from the federation remote instead of
|
|
6
|
+
# importing the module's source directly via the workspace.
|
|
7
|
+
# Run `npm run dev` in modules/gdc-app-template-name/module and set this to the
|
|
8
|
+
# remoteEntry.js URL (typically https://localhost:{modulePort}/remoteEntry.js).
|
|
9
|
+
# APP_TEMPLATE_REMOTE_URL=
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# gdc-app-template-name-harness
|
|
2
|
+
|
|
3
|
+
Standalone development harness for **gdc-app-template-name-module**.
|
|
4
|
+
|
|
5
|
+
This is a template. Run `rush init-pluggable-app` to scaffold a new application from it.
|
|
6
|
+
|
|
7
|
+
## What this does
|
|
8
|
+
|
|
9
|
+
The harness runs the pluggable application module outside of the full host shell. It provides:
|
|
10
|
+
|
|
11
|
+
- A Vite dev server with backend API proxying
|
|
12
|
+
- Local application registration (the module is lazy-loaded the same way the host would load it)
|
|
13
|
+
- GoodData UI Kit CSS and Indigo font
|
|
14
|
+
- HTTPS support via local certificates (optional)
|
|
15
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Copy environment template and fill in your API token
|
|
20
|
+
cp .env.template .env
|
|
21
|
+
|
|
22
|
+
# Start dev server
|
|
23
|
+
rushx dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The server starts at `https://localhost:<port>`. Navigate to the appropriate scope URL:
|
|
27
|
+
|
|
28
|
+
- **Organization-scoped apps**: `https://localhost:<port>/organization/<route>`
|
|
29
|
+
- **Workspace-scoped apps**: `https://localhost:<port>/workspace/<workspaceId>/<route>`
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
| Variable | Default | Description |
|
|
34
|
+
| ----------------- | ------------- | -------------------------------------- |
|
|
35
|
+
| `BACKEND_URL` | _(required)_ | GoodData backend to proxy API calls to |
|
|
36
|
+
| `TIGER_API_TOKEN` | _(required)_ | API token for authentication |
|
|
37
|
+
| `PORT` | {harnessPort} | Dev server port |
|
|
38
|
+
|
|
39
|
+
## Build
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
rushx build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Produces a production bundle in `dist/`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{applicationTemplateTitle} — Harness</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/app.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gdc-app-template-name-harness",
|
|
3
|
+
"version": "11.40.0-alpha.3",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Standalone harness for gdc-app-template-name module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Stellar",
|
|
9
|
+
"email": "stellar@gooddata.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/gooddata/gdc-ui/",
|
|
14
|
+
"directory": "modules/gdc-app-template-name/harness"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
"./tsconfig.json": "./tsconfig.json"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"_phase:build": "npm run build-check",
|
|
22
|
+
"_phase:validate": "npm run validate",
|
|
23
|
+
"build-check": "tsgo",
|
|
24
|
+
"clean": "../../../../common/scripts/clean-command-state.sh && rm -rf dist *.tgz *.log tsconfig.tsbuildinfo",
|
|
25
|
+
"format-check": "oxfmt --check .",
|
|
26
|
+
"format-write": "oxfmt .",
|
|
27
|
+
"lint": "oxlint . --type-aware --quiet && eslint .",
|
|
28
|
+
"lint-fix": "oxlint . --type-aware --quiet --fix && eslint . --fix",
|
|
29
|
+
"validate": "npm run lint && npm run format-check"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@gooddata/sdk-model": "workspace:*",
|
|
33
|
+
"@gooddata/sdk-ui-kit": "workspace:*",
|
|
34
|
+
"@gooddata/sdk-ui-pluggable-host": "workspace:*",
|
|
35
|
+
"gdc-app-template-name-module": "workspace:*",
|
|
36
|
+
"react": "19.1.1",
|
|
37
|
+
"react-dom": "19.1.1",
|
|
38
|
+
"react-intl": "7.1.11",
|
|
39
|
+
"react-router": "7.13.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@gooddata/eslint-config": "workspace:*",
|
|
43
|
+
"@gooddata/oxlint-config": "workspace:*",
|
|
44
|
+
"@types/node": "24.12.0",
|
|
45
|
+
"@types/react": "19.1.11",
|
|
46
|
+
"@types/react-dom": "19.1.7",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "8.58.0",
|
|
48
|
+
"@typescript-eslint/parser": "8.58.0",
|
|
49
|
+
"@typescript/native-preview": "7.0.0-dev.20260202.1",
|
|
50
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
51
|
+
"eslint": "^9.39.2",
|
|
52
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
53
|
+
"eslint-plugin-headers": "1.3.3",
|
|
54
|
+
"eslint-plugin-import-x": "4.16.1",
|
|
55
|
+
"eslint-plugin-jsdoc": "62.1.0",
|
|
56
|
+
"eslint-plugin-no-barrel-files": "1.2.2",
|
|
57
|
+
"eslint-plugin-no-only-tests": "3.3.0",
|
|
58
|
+
"eslint-plugin-react": "7.37.5",
|
|
59
|
+
"eslint-plugin-react-hooks": "5.2.0",
|
|
60
|
+
"eslint-plugin-sonarjs": "3.0.6",
|
|
61
|
+
"jiti": "2.6.1",
|
|
62
|
+
"npm-run-all": "^4.1.5",
|
|
63
|
+
"oxfmt": "0.45.0",
|
|
64
|
+
"oxlint": "^1.43.0",
|
|
65
|
+
"oxlint-tsgolint": "0.11.4",
|
|
66
|
+
"rolldown": "1.0.0-rc.9",
|
|
67
|
+
"typescript": "5.9.3",
|
|
68
|
+
"vite": "8.0.0"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
import "@gooddata/sdk-ui-kit/styles/css/main.css";
|
|
4
|
+
import "@gooddata/sdk-ui-kit/styles/css/indigoFont.css";
|
|
5
|
+
import { createRoot } from "react-dom/client";
|
|
6
|
+
import { BrowserRouter } from "react-router";
|
|
7
|
+
|
|
8
|
+
import { type ApplicationScope, type ILocalPluggableApplicationsRegistryV1 } from "@gooddata/sdk-model";
|
|
9
|
+
import {
|
|
10
|
+
Root,
|
|
11
|
+
registerLocalApplicationLoaders,
|
|
12
|
+
registerLocalApplications,
|
|
13
|
+
} from "@gooddata/sdk-ui-pluggable-host";
|
|
14
|
+
|
|
15
|
+
// The scope placeholder is replaced by `rush init-pluggable-app` with the chosen scope.
|
|
16
|
+
const APP_SCOPE = "{applicationTemplateScope}" as ApplicationScope;
|
|
17
|
+
|
|
18
|
+
// Harness-local app registry. Registers only this module so it can be developed and
|
|
19
|
+
// tested in isolation against a real backend without the full host app.
|
|
20
|
+
//
|
|
21
|
+
// Production: load via Module Federation (matches gdc-host-application).
|
|
22
|
+
// The module's MF artifacts are served from the same nginx container under
|
|
23
|
+
// /{applicationTemplateScope}/remotes/gdc-app-template-name/. Relative path keeps
|
|
24
|
+
// the same-origin security guard happy.
|
|
25
|
+
// Dev: fall back to a local binding so the module's source resolves through
|
|
26
|
+
// the workspace graph without a second dev server. Override with
|
|
27
|
+
// APP_TEMPLATE_REMOTE_URL to point at a running module dev server instead.
|
|
28
|
+
const localApplications: ILocalPluggableApplicationsRegistryV1 = {
|
|
29
|
+
apiVersion: "1.0",
|
|
30
|
+
applications: [
|
|
31
|
+
{
|
|
32
|
+
apiVersion: "1.0",
|
|
33
|
+
id: "gdc-app-template-name",
|
|
34
|
+
applicationScope: APP_SCOPE,
|
|
35
|
+
menuOrder: 1,
|
|
36
|
+
title: "{applicationTemplateTitle}",
|
|
37
|
+
...(PRODUCTION || APP_TEMPLATE_REMOTE_URL
|
|
38
|
+
? {
|
|
39
|
+
remote: {
|
|
40
|
+
url: PRODUCTION
|
|
41
|
+
? "/{applicationTemplateScope}/remotes/gdc-app-template-name/remoteEntry.js"
|
|
42
|
+
: (APP_TEMPLATE_REMOTE_URL as string),
|
|
43
|
+
scope: "{applicationTemplateFederationName}",
|
|
44
|
+
module: "./pluggableApp",
|
|
45
|
+
routeBase: "/app-template-route",
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
: {
|
|
49
|
+
local: { routeBase: "/app-template-route" },
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
registerLocalApplications(localApplications);
|
|
56
|
+
if (!PRODUCTION && !APP_TEMPLATE_REMOTE_URL) {
|
|
57
|
+
registerLocalApplicationLoaders({
|
|
58
|
+
"gdc-app-template-name": () => import("gdc-app-template-name-module"),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// When the harness boots at "/", the host runtime's redirect heuristic prefers
|
|
63
|
+
// "/organization" for users with canManageOrganization permission — which lands a
|
|
64
|
+
// workspace-scoped app on the wrong scope (and renders the wrong header chrome).
|
|
65
|
+
// Force the initial URL into the app's chosen scope before Root mounts.
|
|
66
|
+
if (window.location.pathname === "/" || window.location.pathname === "") {
|
|
67
|
+
const scopeRoot = APP_SCOPE === "workspace" ? "/workspace" : "/organization";
|
|
68
|
+
window.history.replaceState({}, "", scopeRoot + window.location.search + window.location.hash);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rootElement = document.getElementById("root");
|
|
72
|
+
|
|
73
|
+
if (!rootElement) {
|
|
74
|
+
throw new Error("Missing #root element in HTML document.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
createRoot(rootElement).render(
|
|
78
|
+
<BrowserRouter>
|
|
79
|
+
<Root />
|
|
80
|
+
</BrowserRouter>,
|
|
81
|
+
);
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
"jsx": "react-jsx",
|
|
26
|
+
"module": "esnext",
|
|
27
|
+
"moduleResolution": "bundler",
|
|
28
|
+
"noEmit": true,
|
|
29
|
+
"allowJs": true,
|
|
30
|
+
"outDir": "./dist",
|
|
31
|
+
"resolveJsonModule": true,
|
|
32
|
+
"typeRoots": [
|
|
33
|
+
"./node_modules/@types",
|
|
34
|
+
"./typings.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"paths": {
|
|
37
|
+
"react": [
|
|
38
|
+
"./node_modules/@types/react"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"allowSyntheticDefaultImports": true,
|
|
42
|
+
"resolvePackageJsonExports": true,
|
|
43
|
+
"resolvePackageJsonImports": true,
|
|
44
|
+
"useDefineForClassFields": true,
|
|
45
|
+
"noImplicitAny": true,
|
|
46
|
+
"noImplicitThis": true,
|
|
47
|
+
"strictNullChecks": true,
|
|
48
|
+
"strictFunctionTypes": true,
|
|
49
|
+
"strictBindCallApply": true,
|
|
50
|
+
"strictPropertyInitialization": true,
|
|
51
|
+
"strictBuiltinIteratorReturn": true
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"./src/app.tsx",
|
|
55
|
+
"./eslint.config.ts",
|
|
56
|
+
"./typings.d.ts",
|
|
57
|
+
"./vite.config.ts"
|
|
58
|
+
],
|
|
59
|
+
"include": [
|
|
60
|
+
"src",
|
|
61
|
+
"./*",
|
|
62
|
+
"typings.d.ts"
|
|
63
|
+
],
|
|
64
|
+
"exclude": [
|
|
65
|
+
"**/node_modules",
|
|
66
|
+
"**/esm",
|
|
67
|
+
"**/temp",
|
|
68
|
+
"**/dist"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
export declare global {
|
|
4
|
+
export const SDK_BACKEND: "tiger";
|
|
5
|
+
export const PRODUCTION: boolean;
|
|
6
|
+
export const TIGER_API_TOKEN: string | undefined;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* URL of this module's Module Federation remote entry. Injected at build time in dev;
|
|
10
|
+
* in production the harness loads the remote from a relative path on the same origin.
|
|
11
|
+
*/
|
|
12
|
+
export const APP_TEMPLATE_REMOTE_URL: string | undefined;
|
|
13
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import type { ClientRequest } from "http";
|
|
5
|
+
import { join, resolve } from "path";
|
|
6
|
+
|
|
7
|
+
import react from "@vitejs/plugin-react";
|
|
8
|
+
import { type UserConfig, defaultClientConditions, defineConfig, loadEnv } from "vite";
|
|
9
|
+
|
|
10
|
+
// Strip `origin` (Tiger backend rejects cross-origin) and force identity
|
|
11
|
+
// encoding (so the vite proxy can read the response without decompressing).
|
|
12
|
+
function applyProxyRequestHeaders(proxyReq: ClientRequest): void {
|
|
13
|
+
proxyReq.removeHeader("origin");
|
|
14
|
+
proxyReq.setHeader("accept-encoding", "identity");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const certDir = join(process.env["HOME"] || process.env["USERPROFILE"] || "", ".gooddata", "certs");
|
|
18
|
+
let httpsConfig: import("vite").ServerOptions["https"] | undefined;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
httpsConfig = {
|
|
22
|
+
ca: readFileSync(join(certDir, "rootCA.pem")),
|
|
23
|
+
key: readFileSync(join(certDir, "localhost-key.pem")),
|
|
24
|
+
cert: readFileSync(join(certDir, "localhost-cert.pem")),
|
|
25
|
+
};
|
|
26
|
+
// oxlint-disable-next-line @typescript-eslint/no-unused-vars
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.info(`No certificates found in ${certDir}, skipping HTTPS configuration`);
|
|
30
|
+
httpsConfig = undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line no-restricted-exports
|
|
34
|
+
export default defineConfig(({ mode }): UserConfig => {
|
|
35
|
+
const env = loadEnv(mode, process.cwd(), "");
|
|
36
|
+
const isProduction = mode === "production";
|
|
37
|
+
// NOTE: @gooddata/create-pluggable-module's computeDevPort() regex-matches
|
|
38
|
+
// this line shape (`env["PORT"] || "NNNN"`) when scanning existing harnesses
|
|
39
|
+
// to pick a port for the next scaffolded app. If you refactor it, update
|
|
40
|
+
// sdk/tools/create-pluggable-module/src/engine/derived.ts to match.
|
|
41
|
+
const port = parseInt(env["PORT"] || "{harnessPort}");
|
|
42
|
+
const backend = env["BACKEND_URL"];
|
|
43
|
+
if (!isProduction && !backend) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"BACKEND_URL is required for the dev server. Copy .env.template to .env and set BACKEND_URL " +
|
|
46
|
+
"to the URL of your GoodData Cloud instance (e.g. https://yourorg.gooddata.com).",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
root: ".",
|
|
52
|
+
base: isProduction ? "/{applicationTemplateScope}/" : "/",
|
|
53
|
+
publicDir: false,
|
|
54
|
+
define: {
|
|
55
|
+
SDK_BACKEND: JSON.stringify("tiger"),
|
|
56
|
+
TIGER_API_TOKEN: isProduction ? undefined : JSON.stringify(env["TIGER_API_TOKEN"]),
|
|
57
|
+
PRODUCTION: JSON.stringify(isProduction),
|
|
58
|
+
APP_TEMPLATE_REMOTE_URL: isProduction
|
|
59
|
+
? "undefined"
|
|
60
|
+
: env["APP_TEMPLATE_REMOTE_URL"]
|
|
61
|
+
? JSON.stringify(env["APP_TEMPLATE_REMOTE_URL"])
|
|
62
|
+
: "undefined",
|
|
63
|
+
"process.env": JSON.stringify({}),
|
|
64
|
+
REACT_APP_SDK_BACKEND: JSON.stringify("TIGER"),
|
|
65
|
+
REACT_APP_API_TOKEN: isProduction ? undefined : JSON.stringify(env["TIGER_API_TOKEN"]),
|
|
66
|
+
REACT_APP_IN_AIO: JSON.stringify(false),
|
|
67
|
+
},
|
|
68
|
+
plugins: [react()],
|
|
69
|
+
resolve: {
|
|
70
|
+
// resolve.conditions REPLACES Vite's defaults rather than extending them
|
|
71
|
+
// (Vite 8's mergeWithDefaultsRecursively assigns arrays directly), so we must
|
|
72
|
+
// spread defaultClientConditions to keep "browser" / "module" / dev-prod
|
|
73
|
+
// resolution. Without "browser", packages like react-textarea-autosize fall
|
|
74
|
+
// through to their node-targeted default export (a stub that ignores
|
|
75
|
+
// minRows/maxRows).
|
|
76
|
+
conditions: [...defaultClientConditions, "source"],
|
|
77
|
+
dedupe: ["react", "react-dom"],
|
|
78
|
+
},
|
|
79
|
+
server: {
|
|
80
|
+
port,
|
|
81
|
+
https: httpsConfig,
|
|
82
|
+
proxy: {
|
|
83
|
+
"/api": {
|
|
84
|
+
changeOrigin: true,
|
|
85
|
+
cookieDomainRewrite: "localhost",
|
|
86
|
+
secure: false,
|
|
87
|
+
target: backend,
|
|
88
|
+
headers: {
|
|
89
|
+
host: backend,
|
|
90
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
91
|
+
},
|
|
92
|
+
configure: (proxy) => {
|
|
93
|
+
proxy.on("proxyReq", applyProxyRequestHeaders);
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
fs: {
|
|
98
|
+
allow: ["../../../"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
build: {
|
|
102
|
+
outDir: "dist",
|
|
103
|
+
sourcemap: !isProduction,
|
|
104
|
+
target: "es2022",
|
|
105
|
+
chunkSizeWarningLimit: 10000,
|
|
106
|
+
rolldownOptions: {
|
|
107
|
+
input: resolve(__dirname, "index.html"),
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# gdc-app-template-name-module
|
|
2
|
+
|
|
3
|
+
Pluggable application module for **gdc-app-template-name**.
|
|
4
|
+
|
|
5
|
+
This is a template. Run `rush init-pluggable-app` to scaffold a new application from it.
|
|
6
|
+
|
|
7
|
+
## Structure
|
|
8
|
+
|
|
9
|
+
- `src/pluggableApp.tsx` — mount/unmount lifecycle, forwards `onEvent` and `onTelemetryEvent` callbacks
|
|
10
|
+
- `src/AppProviders.tsx` — thin wrapper around `AppProviders` from `@gooddata/sdk-ui-pluggable-application` that binds the app's translation bundle
|
|
11
|
+
- `src/App.tsx` — main React component, consumes platform context, workspace, and localization
|
|
12
|
+
- `src/translations/` — locale JSON files and async loader (`translations.ts`) using `Record<ILocale, ...>` for compile-time enforcement
|
|
13
|
+
|
|
14
|
+
## Development
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Run tests in watch mode
|
|
18
|
+
rushx dev
|
|
19
|
+
|
|
20
|
+
# Run tests once
|
|
21
|
+
rushx test-once
|
|
22
|
+
|
|
23
|
+
# Build
|
|
24
|
+
rushx build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Adding translations
|
|
28
|
+
|
|
29
|
+
1. Add keys to `src/translations/en-US.json` with `{ "text": "...", "crowdinContext": "..." }` format.
|
|
30
|
+
2. Copy the new keys to all other locale JSON files (they start as en-US copies; translators update them later).
|
|
31
|
+
3. The `translations.ts` async loader and `Record<ILocale, ...>` type ensure every locale is covered at build time.
|
|
32
|
+
|
|
33
|
+
## Localization enforcement
|
|
34
|
+
|
|
35
|
+
The `asyncComponentTranslations` map is typed as `Record<ILocale, ...>`. If a new locale is added to `ILocale` in `@gooddata/sdk-model`, the build will fail until a matching JSON file and map entry are added here.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gdc-app-template-name-module",
|
|
3
|
+
"version": "11.40.0-alpha.3",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{applicationTemplateTitle} pluggable application module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Stellar",
|
|
9
|
+
"email": "stellar@gooddata.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/gooddata/gdc-ui/",
|
|
14
|
+
"directory": "modules/gdc-app-template-name/module"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"types": "./src/index.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./src/index.ts",
|
|
22
|
+
"source": "./src/index.ts",
|
|
23
|
+
"default": "./src/index.ts"
|
|
24
|
+
},
|
|
25
|
+
"./tsconfig.json": "./tsconfig.json"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"_phase:build": "npm run build-check",
|
|
29
|
+
"_phase:validate": "npm run validate",
|
|
30
|
+
"build-check": "tsgo",
|
|
31
|
+
"clean": "../../../../common/scripts/clean-command-state.sh && rm -rf esm dist *.tgz *.log tsconfig.tsbuildinfo",
|
|
32
|
+
"format-check": "oxfmt --check .",
|
|
33
|
+
"format-write": "oxfmt .",
|
|
34
|
+
"lint": "oxlint . --type-aware --quiet && eslint .",
|
|
35
|
+
"lint-fix": "oxlint . --type-aware --quiet --fix && eslint . --fix",
|
|
36
|
+
"validate": "npm run lint && npm run format-check"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@gooddata/sdk-model": "workspace:*",
|
|
40
|
+
"@gooddata/sdk-pluggable-application-model": "workspace:*",
|
|
41
|
+
"@gooddata/sdk-ui": "workspace:*",
|
|
42
|
+
"@gooddata/sdk-ui-pluggable-application": "workspace:*",
|
|
43
|
+
"@gooddata/util": "workspace:*",
|
|
44
|
+
"lodash-es": "^4.17.23",
|
|
45
|
+
"react-intl": "7.1.11"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@gooddata/eslint-config": "workspace:*",
|
|
49
|
+
"@gooddata/oxlint-config": "workspace:*",
|
|
50
|
+
"@module-federation/vite": "^1.8.0",
|
|
51
|
+
"@testing-library/dom": "10.4.1",
|
|
52
|
+
"@testing-library/react": "16.3.0",
|
|
53
|
+
"@types/lodash-es": "^4.17.12",
|
|
54
|
+
"@types/node": "24.12.0",
|
|
55
|
+
"@types/react": "19.1.11",
|
|
56
|
+
"@types/react-dom": "19.1.7",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "8.58.0",
|
|
58
|
+
"@typescript-eslint/parser": "8.58.0",
|
|
59
|
+
"@typescript/native-preview": "7.0.0-dev.20260202.1",
|
|
60
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
61
|
+
"eslint": "^9.39.2",
|
|
62
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
63
|
+
"eslint-plugin-headers": "1.3.3",
|
|
64
|
+
"eslint-plugin-import-x": "4.16.1",
|
|
65
|
+
"eslint-plugin-jsdoc": "62.1.0",
|
|
66
|
+
"eslint-plugin-no-barrel-files": "1.2.2",
|
|
67
|
+
"eslint-plugin-no-only-tests": "3.3.0",
|
|
68
|
+
"eslint-plugin-react": "7.37.5",
|
|
69
|
+
"eslint-plugin-react-hooks": "5.2.0",
|
|
70
|
+
"eslint-plugin-sonarjs": "3.0.6",
|
|
71
|
+
"happy-dom": "18.0.1",
|
|
72
|
+
"jiti": "2.6.1",
|
|
73
|
+
"npm-run-all": "^4.1.5",
|
|
74
|
+
"oxfmt": "0.45.0",
|
|
75
|
+
"oxlint": "^1.43.0",
|
|
76
|
+
"oxlint-tsgolint": "0.11.4",
|
|
77
|
+
"react": "19.1.1",
|
|
78
|
+
"react-dom": "19.1.1",
|
|
79
|
+
"rolldown": "1.0.0-rc.9",
|
|
80
|
+
"typescript": "5.9.3",
|
|
81
|
+
"vite": "8.0.0",
|
|
82
|
+
"vitest": "4.1.0"
|
|
83
|
+
}
|
|
84
|
+
}
|