create-ec-app 1.6.0 → 1.8.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.
@@ -0,0 +1,181 @@
1
+ ## Purpose
2
+
3
+ This repository is an Azure Static Web Apps frontend using React, TypeScript, and Vite.
4
+
5
+ Treat it as a static Azure-hosted SPA. Keep it small, readable, and easy to deploy.
6
+
7
+ Use the global AGENTS.md rules first. This file adds project-specific constraints.
8
+
9
+ ## Hard Constraints
10
+
11
+ - Keep Static Web Apps hosting working.
12
+ - Keep local Vite development working.
13
+ - Keep SPA routing fallback working.
14
+ - Keep the app client-side unless an Azure Functions API already exists or is explicitly requested.
15
+ - Make surgical changes.
16
+
17
+ Do not add Dynamics `Xrm`, `token.json`, Power Pages ADAL, or Power Apps code app SDK patterns unless the target changes.
18
+
19
+ ## Runtime Modes
20
+
21
+ The app supports two modes.
22
+
23
+ ### Static Web Apps-hosted
24
+
25
+ - Build output is served from `dist`.
26
+ - Preserve `staticwebapp.config.json`.
27
+ - Keep `navigationFallback` when the app uses client-side routing.
28
+ - Use `staticwebapp.config.json` for SWA routes, auth, headers, response overrides, and fallback rules.
29
+ - Do not add deprecated `routes.json`.
30
+ - Do not rely on client-only route guards for sensitive data. Backend APIs must enforce auth/roles.
31
+
32
+ ### Local dev
33
+
34
+ - Use Vite for normal UI development.
35
+ - Use the Static Web Apps CLI only when testing SWA routing, auth, or `/api` integration locally.
36
+ - Keep `swa-cli.config.json` aligned with the Vite dev server and `dist` output.
37
+ - Do not require a deployed Azure Static Web App for ordinary component work.
38
+
39
+ ## Critical Files
40
+
41
+ | File | Rule |
42
+ |---|---|
43
+ | `staticwebapp.config.json` | Static Web Apps routing/auth/fallback boundary. Preserve SPA fallback unless routing is removed. |
44
+ | `swa-cli.config.json` | Local SWA CLI configuration. Keep `appDevserverUrl`, build command, and output location accurate. |
45
+ | `vite.config.ts` | Preserve React, Tailwind, alias, and any existing build assumptions. |
46
+ | `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
47
+ | `package.json` | Keep SWA CLI scripts/dependencies only if the project uses them. |
48
+ | `api/` | Only add or change when the app actually has a Static Web Apps API requirement. |
49
+
50
+ ## API and Data Access
51
+
52
+ Prefer direct, boring calls to the app's own API or public endpoints.
53
+
54
+ Use:
55
+
56
+ - relative `/api/...` calls for Static Web Apps managed APIs
57
+ - explicit response types near the call site
58
+ - narrow payloads
59
+ - direct `fetch` inside service files
60
+ - clear `response.ok` checks with useful status text
61
+ - `import.meta.env.VITE_*` only for public browser-safe values
62
+
63
+ Avoid:
64
+
65
+ - putting secrets in client-side environment variables
66
+ - direct Dataverse browser calls copied from webresource projects
67
+ - `window.Xrm`
68
+ - Power Pages `_api` assumptions
69
+ - Power Apps generated service assumptions
70
+ - generic API clients for one or two endpoints
71
+ - silent fallbacks for failed required calls
72
+
73
+ If the app needs private data, put the enforcement in the SWA API or configured route auth. Client-side hiding is not security.
74
+
75
+ ## Validation
76
+
77
+ Use TypeScript types for trusted internal data and vendor-shaped responses.
78
+
79
+ Use runtime validation only when the current feature needs it, such as:
80
+
81
+ - user-entered form data
82
+ - URL/search parameters that control behavior
83
+ - local config that can be wrong
84
+ - data that crosses into an API write
85
+ - security-sensitive or data-loss-prone paths
86
+
87
+ Do not validate, normalize, or reformat values just because it is possible.
88
+
89
+ ## Services, Queries, and Mutations
90
+
91
+ Keep service files explicit.
92
+
93
+ Preferred shape:
94
+
95
+ - one fetch/save function for the operation
96
+ - one TanStack Query hook when components need it
97
+ - one mutation hook when mutation state or invalidation is needed
98
+ - query keys colocated with the hook when reused for invalidation
99
+
100
+ Do not create wrapper chains such as:
101
+
102
+ ```text
103
+ resolveConfig -> normalizeInput -> validateInput -> buildRequest -> executeRequest
104
+ ```
105
+
106
+ Prefer direct flow:
107
+
108
+ ```text
109
+ read env/config -> fetch -> check response -> return typed data
110
+ ```
111
+
112
+ ## State Management
113
+
114
+ - Use TanStack Query for server state.
115
+ - Use local component state for local UI behavior.
116
+ - Use Zustand only for shared client state that has outgrown local state.
117
+ - Do not store server state in Zustand.
118
+ - Do not add Redux unless explicitly requested.
119
+
120
+ ## UI and Styling
121
+
122
+ Stay consistent with the project's existing UI system.
123
+
124
+ - Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
125
+ - Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
126
+ - Preserve the existing theme and global CSS imports.
127
+ - Do not mix UI systems unless explicitly asked.
128
+ - Do not hand-roll custom CSS unless component props and Tailwind are not enough.
129
+ - Keep layouts compact, responsive, and suitable for a hosted business app.
130
+
131
+ ## Code Shape
132
+
133
+ Prefer:
134
+
135
+ - focused React components
136
+ - direct typed functions
137
+ - existing services and components
138
+ - small local helpers only when they remove real duplication or name non-obvious domain logic
139
+
140
+ Avoid:
141
+
142
+ - broad factories
143
+ - generic service clients
144
+ - classes for simple service logic
145
+ - excessive configuration
146
+ - defensive wrappers around every value
147
+ - broad refactors while adding a feature
148
+
149
+ ## Error Handling
150
+
151
+ Required reads, saves, deletes, uploads, downloads, auth failures, and required parsing failures should throw.
152
+
153
+ Do not swallow failed fetches and treat them as empty data unless the requirement explicitly says the feature is best-effort.
154
+
155
+ Include response status and useful response text where practical.
156
+
157
+ ## Build and Deployment
158
+
159
+ Do not replace Vite, add SSR, add Next.js, or introduce backend coupling unless explicitly asked.
160
+
161
+ Keep `staticwebapp.config.json` deployable at the output root. If the build flow changes, verify the SWA config still reaches `dist`.
162
+
163
+ ## Checks
164
+
165
+ Run the smallest relevant command for the changed area, such as:
166
+
167
+ - typecheck
168
+ - lint
169
+ - targeted tests
170
+ - Vite build when deployment shape could be affected
171
+ - SWA CLI smoke test when `staticwebapp.config.json` or `/api` routing changes
172
+
173
+ Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
174
+
175
+ ## Figma MCP
176
+
177
+ When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
178
+
179
+ - Ensure responsiveness on all screen sizes
180
+ - If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
181
+ - Use the exact colours in the design. Don't make up your own.
@@ -1,131 +1,242 @@
1
1
  ## Purpose
2
2
 
3
- This is a Dynamics 365 / Dataverse web resource template using React + TypeScript + Vite.
4
- Treat it as a **Dynamics-hosted frontend** — not a generic SPA. Do not modernise it into something else unless explicitly asked.
3
+ This repository is a Dynamics 365 / Dataverse web resource using React, TypeScript, and Vite.
5
4
 
6
- The default philosophy is: keep the app small, readable, and easy to ship into Dynamics. Prefer obvious code, existing components, and the template's established runtime patterns over broad abstractions or clever defensive layers.
5
+ Treat it as a Dynamics-hosted frontend, not a generic SPA. Keep it small, readable, and easy to ship into Dynamics.
7
6
 
8
- ---
7
+ Use the global AGENTS.md rules first. This file adds project-specific constraints.
9
8
 
10
9
  ## Hard Constraints
11
10
 
12
- 1. **Dynamics runtime must keep working** — preserve `window.parent.Xrm`/`window.top.Xrm` detection and `ClientGlobalContext.js.aspx` where present.
13
- 2. **Local dev must keep working** — `token.json` drives local auth; never bundle it into output or commit real values.
14
- 3. **Build output must stay web-resource-friendly** — predictable filenames, no uncontrolled chunking, deployable via Webresource Manager or pipeline upload.
15
- 4. **Keep it client-side** — no SSR, no Next.js, no backend coupling unless explicitly requested.
16
- 5. **Make surgical changes** — extend existing patterns before inventing new ones; don't refactor unrelated areas.
11
+ - Keep Dynamics runtime support working.
12
+ - Keep local development support working.
13
+ - Keep build output web-resource-friendly.
14
+ - Keep the app client-side.
15
+ - Make surgical changes.
17
16
 
18
- ---
17
+ Do not modernize the project into a different architecture unless explicitly asked.
19
18
 
20
19
  ## Runtime Modes
21
20
 
22
- **Dynamics-hosted:** detect via `window.parent.Xrm`, derive base URL from `getGlobalContext().getClientUrl()`, no bearer token needed.
21
+ The app supports two modes.
23
22
 
24
- **Local dev:** load `token.json` dynamically, use bearer token, call Dataverse directly.
23
+ ### Dynamics-hosted
25
24
 
26
- Never mix the two modes or duplicate their logic — reuse `authService.ts`.
25
+ - Detect Dynamics through the existing `window.parent.Xrm` / `window.top.Xrm` pattern.
26
+ - Derive the base URL from `Xrm.Utility.getGlobalContext().getClientUrl()`.
27
+ - Do not add bearer-token auth in hosted mode.
28
+ - Preserve `ClientGlobalContext.js.aspx` where the project uses it.
27
29
 
28
- ---
30
+ ### Local dev
29
31
 
30
- ## Critical Files Don't Break These
32
+ - Use `token.json` for local auth only.
33
+ - Load `token.json` dynamically.
34
+ - Never commit real token values.
35
+ - Never bundle `token.json` into deployment output.
31
36
 
32
- | File | Why it matters |
37
+ Do not mix the two modes. Do not duplicate runtime detection. Reuse `authService.ts`.
38
+
39
+ ## Critical Files
40
+
41
+ | File | Rule |
33
42
  |---|---|
34
- | `src/services/authService.ts` | Single source of truth for env detection, base URL, and auth headers |
35
- | `src/main.tsx` | App bootstrap — preserve providers and theme imports |
36
- | `vite.config.ts` | Controls deployment shape: `base: "./"`, predictable output names, `main.css` |
37
- | `index.html` | Dynamics integration boundary may inject `ClientGlobalContext.js.aspx` |
38
- | `token.json` | Local dev only never commit real values, never bundle |
43
+ | `src/services/authService.ts` | Single source of truth for runtime detection, base URL, and auth headers. |
44
+ | `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
45
+ | `vite.config.ts` | Preserve Dynamics-friendly output: `base: "./"`, predictable filenames, and `main.css`. |
46
+ | `index.html` | Treat as the Dynamics integration boundary. Preserve `ClientGlobalContext.js.aspx` where present. |
47
+ | `token.json` | Local dev only. Never commit real values or bundle it. |
39
48
 
40
- ---
49
+ ## API and Data Access
41
50
 
42
- ## API / Data Access
51
+ Prefer direct, boring Dataverse Web API calls.
43
52
 
44
- - Put reusable API logic in `src/services`
45
- - Always reuse `getApiUrl()` and `getAuthHeaders()` — never duplicate them
46
- - Use narrow `$select` queries; throw on non-OK responses
47
- - No raw fetch calls scattered across UI components
48
- - Use Zod for validation where data crosses a boundary: form inputs, URL/search params, config, and Dataverse/API responses that the UI depends on
49
- - Keep service functions boring and explicit: one fetch function, one hook, one mutation where needed
50
- - Do not add repository/client layers unless the app has enough repeated API logic to justify them
53
+ Use:
51
54
 
52
- **Preferred pattern:**
53
- ```ts
54
- // {entity}Service.ts fetch function + TanStack Query hook
55
- // invalidate relevant queryKey on mutation success
55
+ - `getApiUrl()`
56
+ - `getAuthHeaders()`
57
+ - narrow `$select` queries
58
+ - `URLSearchParams` for normal query parameters
59
+ - direct `fetch` inside service files
60
+ - small TypeScript interfaces for response shapes
61
+ - clear `response.ok` checks with useful status text
62
+
63
+ Avoid:
64
+
65
+ - raw fetch calls inside UI components
66
+ - duplicated auth or base URL logic
67
+ - repository/client layers for small features
68
+ - metadata resolvers unless arbitrary table names are a real requirement
69
+ - entity caches unless repeated metadata/API cost is demonstrated
70
+ - generic OData builders
71
+ - Zod schemas for every Dataverse response
72
+ - GUID or logical-name regex validation by default
73
+ - silent fallbacks for failed Dataverse calls
74
+
75
+ For known tables, use known entity set names. Fetch metadata only when the feature truly supports arbitrary entity logical names.
76
+
77
+ Escape OData string literals when interpolating inside quoted OData expressions. Do not create a broad escaping/parsing layer for simple queries.
78
+
79
+ ## Validation
80
+
81
+ Use TypeScript types for normal Dataverse response shapes.
82
+
83
+ Use runtime validation only when the current feature needs it, such as:
84
+
85
+ - user-entered form data
86
+ - URL/search parameters that control behavior
87
+ - local config that can be wrong
88
+ - genuinely variable API data where the UI must branch safely
89
+ - security-sensitive or data-loss-prone paths
90
+
91
+ Do not validate values just because they are shaped like GUIDs, logical names, dates, URLs, or enum strings. If Dataverse will reject the value clearly and there is no local UX/security need, pass it through.
92
+
93
+ Do not normalize strings by default. Trim, lowercase, strip braces, or reformat only when the app has a known input source that sends multiple formats.
94
+
95
+ ## Services, Queries, and Mutations
96
+
97
+ Keep service files explicit.
98
+
99
+ Preferred shape:
100
+
101
+ - one fetch/save function for the operation
102
+ - one TanStack Query hook when components need it
103
+ - one mutation hook when mutation state or invalidation is needed
104
+ - query keys colocated with the hook when reused for invalidation
105
+
106
+ Do not create wrapper chains such as:
107
+
108
+ ```text
109
+ resolveConfig -> normalizeInput -> validateInput -> resolveMetadata -> buildRequest -> executeRequest
56
110
  ```
57
111
 
58
- See the example at the bottom of this file.
112
+ Prefer direct flow:
59
113
 
60
- ---
114
+ ```text
115
+ read config -> fetch -> check response -> return typed data
116
+ ```
61
117
 
62
118
  ## State Management
63
119
 
64
- - **TanStack Query** server state
65
- - **Zustand** shared client state
66
- - **Local component state** local UI behaviour
67
- - No Redux unless explicitly requested; don't store server state in Zustand
68
-
69
- ---
120
+ - Use TanStack Query for server state.
121
+ - Use local component state for local UI behavior.
122
+ - Use Zustand only for shared client state that has outgrown local state.
123
+ - Do not store server state in Zustand.
124
+ - Do not add Redux unless explicitly requested.
70
125
 
71
- ## UI & Styling
126
+ ## UI and Styling
72
127
 
73
- - Kendo UI or Shadcn/ui — stay consistent with whichever the project uses; don't mix UI systems unless explicitly asked.
74
- - If the project uses Shadcn/ui, use Shadcn components from `@/components/ui` and style them with Tailwind utility classes.
75
- - If the project uses Kendo UI, use Kendo React components for controls, grids, menus, dialogs, inputs, and other rich UI. Use Tailwind for layout, spacing, and local composition around those components.
76
- - Tailwind is the default styling approach for both Shadcn/ui and Kendo projects; preserve `main.css` output.
77
- - Do not hand-roll component styling or custom CSS unless Tailwind/component props cannot reasonably express the requirement.
78
- - Prefer existing component APIs, theme tokens, variants, and utility helpers before creating new wrappers.
79
- - Keep screen layouts practical for embedded Dynamics use: compact, scannable, responsive, and not marketing-page styled.
128
+ Stay consistent with the project's existing UI system.
80
129
 
81
- ---
130
+ - Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
131
+ - Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
132
+ - Preserve the existing theme and `main.css` output.
133
+ - Do not mix UI systems unless explicitly asked.
134
+ - Do not hand-roll custom CSS unless component props and Tailwind are not enough.
135
+ - Keep layouts compact, scannable, responsive, and suitable for embedded Dynamics screens.
82
136
 
83
137
  ## Code Shape
84
138
 
85
- - Keep code simple and readable. A future maintainer should understand the main path quickly.
86
- - Avoid over-engineering: no generic frameworks, broad factories, speculative abstractions, or excessive configuration for small features.
87
- - Avoid being overly defensive. Validate real external inputs and API responses where useful, but don't wrap every local value in ceremony.
88
- - Prefer direct, typed functions over classes unless the existing code already uses classes for that concern.
89
- - Keep React components focused: UI in components, reusable data access in services, shared client state in Zustand only when local state is no longer enough.
90
- - Make the smallest change that solves the request cleanly.
139
+ Prefer:
91
140
 
92
- ---
141
+ - focused React components
142
+ - direct typed functions
143
+ - existing services and components
144
+ - small local helpers only when they remove real duplication or name non-obvious domain logic
145
+ - explicit Dataverse table/field handling over generic frameworks
93
146
 
94
- ## What To Avoid
147
+ Avoid:
95
148
 
96
- Unless explicitly asked: don't replace Vite, don't add SSR, don't remove Dynamics runtime logic, don't remove the local token flow, don't hardcode org-specific values, don't rename output files in ways that complicate deployment.
149
+ - broad factories
150
+ - generic service clients
151
+ - classes for simple service logic
152
+ - excessive configuration
153
+ - defensive wrappers around every value
154
+ - broad refactors while adding a feature
97
155
 
98
- ---
156
+ ## Error Handling
157
+
158
+ Dataverse reads, saves, deletes, uploads, downloads, auth failures, and required parsing failures should throw.
159
+
160
+ Do not swallow failed fetches and treat them as “not found” unless the requirement explicitly says the feature is best-effort.
161
+
162
+ Include response status and useful response text where practical.
163
+
164
+ ## Build and Deployment
165
+
166
+ Do not replace Vite, add SSR, add Next.js, change output names, enable uncontrolled chunking, or introduce backend coupling unless explicitly asked.
167
+
168
+ Keep output deployable through Webresource Manager or the existing pipeline.
169
+
170
+ ## Checks
171
+
172
+ Run the smallest relevant command for the changed area, such as:
173
+
174
+ - typecheck
175
+ - lint
176
+ - targeted tests
177
+ - Vite build when deployment shape could be affected
178
+
179
+ Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
99
180
 
100
181
  ## Example Service Pattern
101
182
 
102
183
  ```ts
103
- import { getApiUrl, getAuthHeaders } from "@/services/authService";
104
184
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
105
185
 
186
+ import { getApiUrl, getAuthHeaders } from "@/services/authService";
187
+
106
188
  export interface Account {
107
189
  accountid: string;
108
190
  name?: string | null;
109
191
  }
110
192
 
111
193
  export const listAccounts = async (): Promise<Account[]> => {
112
- const res = await fetch(
194
+ const response = await fetch(
113
195
  `${getApiUrl()}/accounts?$select=accountid,name&$top=50`,
114
196
  { headers: await getAuthHeaders() },
115
197
  );
116
- if (!res.ok) throw new Error(`Failed to fetch accounts: ${res.status}`);
117
- return (await res.json()).value as Account[];
198
+
199
+ if (!response.ok) {
200
+ throw new Error(`Failed to fetch accounts: ${response.status}`);
201
+ }
202
+
203
+ const data = (await response.json()) as { value: Account[] };
204
+ return data.value;
118
205
  };
119
206
 
207
+ const accountsQueryKey = ["accounts"] as const;
208
+
120
209
  export const useAccounts = () =>
121
- useQuery({ queryKey: ["accounts"], queryFn: listAccounts });
210
+ useQuery({ queryKey: accountsQueryKey, queryFn: listAccounts });
211
+
212
+ export const patchAccount = async (
213
+ id: string,
214
+ data: Partial<Account>,
215
+ ): Promise<void> => {
216
+ const response = await fetch(`${getApiUrl()}/accounts(${id})`, {
217
+ method: "PATCH",
218
+ headers: await getAuthHeaders(),
219
+ body: JSON.stringify(data),
220
+ });
221
+
222
+ if (!response.ok) {
223
+ throw new Error(`Failed to update account: ${response.status}`);
224
+ }
225
+ };
122
226
 
123
227
  export const useUpdateAccount = () => {
124
228
  const queryClient = useQueryClient();
229
+
125
230
  return useMutation({
126
231
  mutationFn: ({ id, data }: { id: string; data: Partial<Account> }) =>
127
232
  patchAccount(id, data),
128
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ["accounts"] }),
233
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: accountsQueryKey }),
129
234
  });
130
235
  };
131
236
  ```
237
+ ## Figma MCP
238
+ When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
239
+
240
+ - Ensure responsiveness on all screen sizes
241
+ - If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
242
+ - Use the exact colours in the design. Don't make up your own.
@@ -270,10 +270,15 @@ Basic flow:
270
270
  npm run build
271
271
  ```
272
272
 
273
- 2. Run the generator and point `--pcf-dir` at the generated PCF project directory:
273
+ 2. Run the generator from the webresource root. Point `--pcf-dir` at the webresource root and `--output` at the PCF project folder you want to generate:
274
274
 
275
275
  ```bash
276
- npx create-ec-app@latest --pcf-dir ./pcf/{{ControlName}} namespace {{EC}} --constructor {{ControlName}} --display-name "Control Name"
276
+ npx create-ec-app@latest \
277
+ --pcf-dir . \
278
+ --output ./pcf/{{ControlName}} \
279
+ --namespace EC \
280
+ --constructor {{ControlName}} \
281
+ --display-name "Control Name"
277
282
  ```
278
283
 
279
284
  3. Install dependencies inside that generated PCF directory:
@@ -281,33 +286,41 @@ npx create-ec-app@latest --pcf-dir ./pcf/{{ControlName}} namespace {{EC}} --cons
281
286
  ```bash
282
287
  cd ./pcf/{{ControlName}}
283
288
  npm install
289
+ npm run build
284
290
  ```
285
291
 
286
292
  This writes a standalone PCF project to the `--pcf-dir` folder. The generated control:
287
293
 
288
294
  - imports `src/App.tsx` directly instead of wrapping built HTML in an iframe
289
295
  - creates and imports `pcf-scoped.css` from the built `dist/main.css`
296
+ - scopes every non-keyframe CSS selector under the generated PCF host selector
290
297
  - creates `src/runtime/types.ts` only if that file does not already exist
291
298
  - provides a runtime object with record context and `context.webAPI` access inside the generated PCF shell, following the `PcfBase` pattern
292
299
  - mounts your React app directly into the PCF container
293
300
 
294
- Typical conversion flow from inside a generated webresource project:
301
+ Regenerate after app code or CSS changes by running the same sequence again from the webresource root:
295
302
 
296
303
  ```bash
297
- npm install
298
304
  npm run build
299
- npx create-ec-app@latest --pcf-dir ./pcf/FusionNotebookHost namespace EC --constructor FusionNotebookHost --display-name "Fusion Notebook Host"
305
+ npx create-ec-app@latest \
306
+ --pcf-dir . \
307
+ --output ./pcf/FusionNotebookHost \
308
+ --namespace EC \
309
+ --constructor FusionNotebookHost \
310
+ --display-name "Fusion Notebook Host"
300
311
  cd pcf/FusionNotebookHost
301
312
  npm install
302
313
  npm run build
303
314
  ```
304
315
 
316
+ Regeneration removes and recreates the PCF output folder, so keep durable app code in `src` and use generator templates or layers for repeatable PCF-specific changes.
317
+
305
318
  What gets generated:
306
319
 
307
320
  - a minimal PCF wrapper project under `pcf/<ConstructorName>`
308
321
  - a checked-in PCF shell stamped out from `create-ec-app/templates/pcf/base`
309
322
  - direct imports back to your webresource source
310
- - a generated `pcf-scoped.css` file with CSS custom properties scoped to the PCF control
323
+ - a generated `pcf-scoped.css` file with CSS selectors scoped to the PCF control
311
324
 
312
325
  ## Notes
313
326
 
@@ -4,7 +4,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
  import "@progress/kendo-theme-fluent/dist/all.css";
5
5
  import "./index.css";
6
6
  import App from "./App.tsx";
7
- import { EcAppShell } from "./runtime/EcAppShell.tsx";
8
7
 
9
8
  const queryClient = new QueryClient({
10
9
  defaultOptions: {
@@ -24,9 +23,7 @@ const root = createRoot(document.getElementById("root")!);
24
23
  root.render(
25
24
  <StrictMode>
26
25
  <QueryClientProvider client={queryClient}>
27
- <EcAppShell>
28
- <App />
29
- </EcAppShell>
26
+ <App />
30
27
  </QueryClientProvider>
31
28
  </StrictMode>
32
29
  );
@@ -1,21 +0,0 @@
1
- import * as React from "react";
2
-
3
- const EcPortalContainerContext = React.createContext<HTMLElement | null>(null);
4
-
5
- export function useEcPortalContainer() {
6
- return React.useContext(EcPortalContainerContext);
7
- }
8
-
9
- export function EcAppShell({ children }: { children: React.ReactNode }) {
10
- const [portalContainer, setPortalContainer] =
11
- React.useState<HTMLDivElement | null>(null);
12
-
13
- return (
14
- <div data-ec-app-root="">
15
- <EcPortalContainerContext.Provider value={portalContainer}>
16
- {children}
17
- <div data-ec-portal-root="" ref={setPortalContainer} />
18
- </EcPortalContainerContext.Provider>
19
- </div>
20
- );
21
- }