@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
package/esm/types.d.ts ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Contracts shared between the engine (engine/runProfile.ts and friends) and the
3
+ * profile authors (profiles/client.ts, profiles/internal.ts). The engine drives
4
+ * the *order* of operations (prompt → derive → copy → register → postCopy); the
5
+ * profile fills in *what* each step does.
6
+ *
7
+ * The IScaffoldProfile interface is small on purpose: a new profile is a
8
+ * single file that fills in these fields, and diffing two profiles tells you
9
+ * exactly how they diverge.
10
+ */
11
+ export type ProfileName = "client" | "internal";
12
+ export type ApplicationScope = "workspace" | "organization";
13
+ export type PackageManager = "npm" | "yarn" | "pnpm" | "rush";
14
+ /**
15
+ * Output of engine/repoDetection.ts. Computed once at startup from `cwd`,
16
+ * then threaded through the rest of the pipeline so neither profile nor
17
+ * registrations need to redo filesystem probes.
18
+ *
19
+ * Profiles that need to gate on "is this a Rush monorepo" check
20
+ * `packageManager === "rush"` — there's no separate boolean to keep in sync.
21
+ */
22
+ export interface IRepoInfo {
23
+ /** Absolute path resolved from process.cwd(). */
24
+ repoRoot: string;
25
+ /** Detected package manager — drives postCopy. */
26
+ packageManager: PackageManager;
27
+ }
28
+ /**
29
+ * Answers populated by the engine via the shared prompts (appName, title, scope).
30
+ * Profile-specific extra prompts (e.g. `maintainer` for internal, `destPath` for
31
+ * client) write into the same object under their declared `key`.
32
+ *
33
+ * The index signature is intentional — profiles narrow with a local cast in
34
+ * their callbacks since they know which extras they registered. Trying to make
35
+ * this a discriminated union forces a generic parameter through the whole
36
+ * engine for very little safety gain at this scale.
37
+ */
38
+ export interface IScaffoldAnswers {
39
+ appName: string;
40
+ title: string;
41
+ scope: ApplicationScope;
42
+ [extraKey: string]: string | undefined;
43
+ }
44
+ export interface IPromptDescriptor {
45
+ /** Key under which the answer lands in IScaffoldAnswers. */
46
+ key: string;
47
+ /** Question text shown via readline. The engine appends ": ". */
48
+ label: string;
49
+ /**
50
+ * Whether an empty answer is accepted (true) or re-prompts the user (false, default).
51
+ * When optional, the answer slot is left undefined; profile callbacks must handle that.
52
+ */
53
+ optional?: boolean;
54
+ /** Returns null on success, or an error string to display and re-prompt. */
55
+ validate?: (value: string) => string | null;
56
+ }
57
+ export interface IDerivedValues {
58
+ /** Default route under which the host mounts the app, e.g. "/foo" for gdc-foo. */
59
+ route: string;
60
+ /** Module Federation `name` — a valid JS identifier derived from appName. */
61
+ federationName: string;
62
+ /** Compile-time env var the host/harness reads to override the dev remoteEntry URL. */
63
+ remoteUrlEnvVar: string;
64
+ /** Port the harness vite dev server listens on. */
65
+ devPort: number;
66
+ /** Port the module federation preview server listens on (= devPort + 100). */
67
+ moduleDevPort: number;
68
+ /** Position in the host menu among apps of the same scope. */
69
+ menuOrder: number;
70
+ }
71
+ export interface IProfileContext {
72
+ answers: IScaffoldAnswers;
73
+ derived: IDerivedValues;
74
+ /** Absolute path to the repo the engine is scaffolding into. */
75
+ repoRoot: string;
76
+ /** Absolute destination root for the new module, e.g. <repoRoot>/modules/<appName>/. */
77
+ destRoot: string;
78
+ /** Detected package manager — drives postCopy. */
79
+ packageManager: PackageManager;
80
+ }
81
+ /**
82
+ * Ordered list of token → replacement pairs applied to every text file in a
83
+ * template subtree. ORDER MATTERS:
84
+ * - longer tokens MUST come before shorter ones that are their substrings
85
+ * (e.g. `gdc-app-template-module` before `gdc-app-template`),
86
+ * - we use an array (not Record<string, string>) so JS-engine integer-key
87
+ * iteration ordering can't reshuffle numeric tokens (`"8450"`, `"8550"`)
88
+ * ahead of string tokens.
89
+ */
90
+ export type TokenReplacements = ReadonlyArray<readonly [string, string]>;
91
+ /**
92
+ * A single anchor-driven insertion into an existing repo file. The `emit`
93
+ * callback returns the literal lines (without trailing newlines); the engine
94
+ * splices them in before the line containing PLUGGABLE_APP_SCAFFOLD_ANCHOR
95
+ * (suffixed if `suffix` is set).
96
+ *
97
+ * `preInsertTransform` runs against the file content BEFORE the splice — used
98
+ * by JSONC files (rush.json) to add a trailing comma to the last existing
99
+ * project entry so the spliced array remains valid JSON.
100
+ */
101
+ export interface IRegistrationStep {
102
+ /** Path relative to repoRoot. */
103
+ file: string;
104
+ /**
105
+ * Disambiguating suffix when a file carries more than one anchor point
106
+ * (e.g. Dockerfile has both `(stages)` and `(remotes)` anchors).
107
+ */
108
+ suffix?: string;
109
+ preInsertTransform?: (content: string, suffix: string | undefined) => string;
110
+ emit: (ctx: IProfileContext) => string[];
111
+ }
112
+ /**
113
+ * Additional file tree layered on top of the scaffolded templateSubtrees. Used
114
+ * by profiles that need files which shouldn't live in the shared template —
115
+ * e.g. internal-only deploy configs that would leak GoodData infra if shipped
116
+ * to client repos. Profile authors point `srcDir` at their own bundled assets
117
+ * (typically next to the profile's own source file) so the files physically
118
+ * cannot reach the published template tarball.
119
+ *
120
+ * Token substitution applies the same as for `templateSubtrees`. Overlay files
121
+ * with the same path as a template file overwrite the template version.
122
+ */
123
+ export interface IOverlayPath {
124
+ /** Absolute path to a directory whose contents are copied into destRoot/destSubdir. */
125
+ srcDir: string;
126
+ /** Subdirectory under destRoot where the overlay contents land (e.g. "harness"). */
127
+ destSubdir: string;
128
+ }
129
+ export interface IScaffoldProfile {
130
+ readonly name: ProfileName;
131
+ /** Prompts displayed after the shared appName/title/scope prompts, in array order. */
132
+ readonly extraPrompts: readonly IPromptDescriptor[];
133
+ /** Resolves where the scaffolded files will land. Receives raw cwd, not repoRoot. */
134
+ resolveDestRoot(answers: IScaffoldAnswers, cwd: string): string;
135
+ /**
136
+ * Subdirectory names under src/templates/ that this profile wants copied into destRoot.
137
+ * Engine copies them in array order, so put `module` before `harness` if harness imports module.
138
+ */
139
+ readonly templateSubtrees: readonly string[];
140
+ /**
141
+ * Optional override: explicit absolute filesystem paths for each subtree.
142
+ * When set, the engine uses these directly and skips its own resolution.
143
+ * Profile authors use this for internal-only profiles where the templates
144
+ * aren't installed as transitive deps of @gooddata/create-pluggable-module —
145
+ * e.g. gdc-ui's internal scaffolder resolves the workspace templates via
146
+ * its own `import.meta.url`-relative path.
147
+ *
148
+ * Keys must be a superset of `templateSubtrees`. Subtrees without an entry
149
+ * here fall back to the bundled `dist/templates/<subtree>/` (created by
150
+ * prepack-bundle-template.sh). If neither is available, the engine throws.
151
+ */
152
+ readonly templateSubtreeRoots?: Readonly<Record<string, string>>;
153
+ /**
154
+ * Optional overlays applied after `templateSubtrees`. See `IOverlayPath`.
155
+ * Profile authors use this to keep profile-specific files out of the shared
156
+ * template (which would otherwise leak them at publish time).
157
+ */
158
+ readonly overlayPaths?: readonly IOverlayPath[];
159
+ /**
160
+ * When true, the engine resolves templates from the workspace source (with
161
+ * workspace-extending tsconfigs etc.) instead of the bundled `dist/templates/`
162
+ * (with inlined tsconfigs). Set true for internal-only profiles that scaffold
163
+ * into a repo that already has the workspace tsconfigs at the expected paths
164
+ * (e.g. gdc-ui's own `modules/tsconfig.*.json`). Default: false — clients
165
+ * scaffolding into arbitrary repos need the self-contained bundled version.
166
+ */
167
+ readonly preferWorkspaceTemplate?: boolean;
168
+ /** Derives route, ports, menu order, federation name, etc. Runs after prompts, before copy. */
169
+ derive(answers: IScaffoldAnswers, repo: IRepoInfo): IDerivedValues;
170
+ /** Ordered token → replacement pairs applied to every text file under each subtree on copy. */
171
+ buildReplacements(ctx: IProfileContext): TokenReplacements;
172
+ /**
173
+ * In-place transform of each copied module/harness package.json. Used by the
174
+ * client profile to rewrite `workspace:*` deps to concrete semver — internal
175
+ * leaves them as `workspace:*`.
176
+ */
177
+ transformPackageJson(pkg: Record<string, unknown>, ctx: IProfileContext): Record<string, unknown>;
178
+ /**
179
+ * Files in the host repo that must be edited to register the new app
180
+ * (rush.json, host registry, dockerfiles, etc.). Single source of truth —
181
+ * the engine uses this list for both the pre-flight anchor check AND the
182
+ * actual insertion pass, so the two can't drift.
183
+ *
184
+ * Returns empty for fully-decoupled scaffolds.
185
+ *
186
+ * Profile authors use `repo` to gate steps on filesystem state (e.g. client
187
+ * skips the rush.json registration unless `repo.packageManager === "rush"`).
188
+ * The same list is consumed by pre-flight `checkAnchors` (before prompts/derive)
189
+ * and by the post-copy insertion pass, so don't make this depend on answers.
190
+ */
191
+ registrations(repo: IRepoInfo): readonly IRegistrationStep[];
192
+ /** Final step after copy + register: rush update / npm install / no-op. */
193
+ postCopy(ctx: IProfileContext): Promise<void>;
194
+ }
package/esm/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // (C) 2026 GoodData Corporation
2
+ export {};
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@gooddata/create-pluggable-module",
3
+ "version": "11.40.0-alpha.3",
4
+ "description": "Scaffolder for GoodData pluggable applications. Invoked via `npm init @gooddata/pluggable-module`.",
5
+ "license": "MIT",
6
+ "author": "GoodData Corporation",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gooddata/gooddata-ui-sdk.git",
10
+ "directory": "tools/create-pluggable-module"
11
+ },
12
+ "bin": {
13
+ "create-pluggable-module": "./esm/index.js"
14
+ },
15
+ "files": [
16
+ "esm/**/*.js",
17
+ "esm/**/*.json",
18
+ "esm/**/*.d.ts",
19
+ "esm/**/*.map",
20
+ "dist/templates/**"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "main": "./esm/index.js",
25
+ "types": "./esm/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./esm/index.d.ts",
29
+ "default": "./esm/index.js"
30
+ }
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "24.12.0",
34
+ "@typescript-eslint/eslint-plugin": "8.58.0",
35
+ "@typescript-eslint/parser": "8.58.0",
36
+ "@typescript/native-preview": "7.0.0-dev.20260202.1",
37
+ "eslint": "^9.39.2",
38
+ "eslint-import-resolver-typescript": "4.4.4",
39
+ "eslint-plugin-headers": "1.3.3",
40
+ "eslint-plugin-import-esm": "1.2.1",
41
+ "eslint-plugin-import-x": "4.16.1",
42
+ "eslint-plugin-jsdoc": "62.1.0",
43
+ "eslint-plugin-no-barrel-files": "1.2.2",
44
+ "eslint-plugin-sonarjs": "3.0.6",
45
+ "npm-run-all": "^4.1.5",
46
+ "oxfmt": "0.45.0",
47
+ "oxlint": "^1.43.0",
48
+ "oxlint-tsgolint": "0.11.4",
49
+ "tslib": "2.8.1",
50
+ "typescript": "5.9.3",
51
+ "vite": "8.0.0",
52
+ "vitest": "4.1.0",
53
+ "@gooddata/oxlint-config": "11.40.0-alpha.3",
54
+ "@gooddata/eslint-config": "11.40.0-alpha.3",
55
+ "gdc-app-template-name-harness": "11.40.0-alpha.3",
56
+ "gdc-app-template-name-module": "11.40.0-alpha.3"
57
+ },
58
+ "engines": {
59
+ "node": ">=24.12.0"
60
+ },
61
+ "scripts": {
62
+ "_phase:build": "npm run build",
63
+ "_phase:test": "npm run test-once",
64
+ "_phase:validate": "npm run validate",
65
+ "build": "npm-run-all -p build-check build-ts",
66
+ "build-check": "tsgo",
67
+ "build-ts": "tsgo -p tsconfig.build.json",
68
+ "clean": "../../common/scripts/clean-command-state.sh && rm -rf ci dist esm coverage *.log tsconfig.tsbuildinfo",
69
+ "format-check": "oxfmt --check .",
70
+ "format-write": "oxfmt .",
71
+ "lint": "oxlint . --type-aware --quiet && eslint src",
72
+ "lint-fix": "oxlint . --type-aware --quiet --fix && eslint src --fix",
73
+ "test": "vitest watch",
74
+ "test-once": "vitest run --passWithNoTests",
75
+ "validate": "npm run lint && npm run format-check"
76
+ }
77
+ }