create-ec-app 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +86 -21
  2. package/dist/cssScope.d.ts +4 -0
  3. package/dist/cssScope.d.ts.map +1 -0
  4. package/dist/cssScope.js +124 -0
  5. package/dist/cssScope.js.map +1 -0
  6. package/dist/index.js +28 -94
  7. package/dist/index.js.map +1 -1
  8. package/dist/pcf.d.ts.map +1 -1
  9. package/dist/pcf.js +13 -84
  10. package/dist/pcf.js.map +1 -1
  11. package/dist/portalContainers.d.ts +7 -0
  12. package/dist/portalContainers.d.ts.map +1 -0
  13. package/dist/portalContainers.js +118 -0
  14. package/dist/portalContainers.js.map +1 -0
  15. package/package.json +6 -3
  16. package/scripts/check-generated-css-scope.mjs +98 -37
  17. package/templates/base/src/App.tsx +1 -1
  18. package/templates/base/src/index.css +1 -4
  19. package/templates/base/src/main.tsx +1 -4
  20. package/templates/pcf/base/README.md +28 -9
  21. package/templates/pcf/base/index.ts +30 -6
  22. package/templates/pcf/base/runtime/PcfAppShell.tsx +17 -0
  23. package/templates/pcf/base/runtime/emptyStyles.js +1 -0
  24. package/templates/pcf/base/webpack.config.js +13 -0
  25. package/templates/targets/code-apps/README.md +246 -0
  26. package/templates/targets/code-apps/package.patch.json +8 -0
  27. package/templates/targets/code-apps/power.config.example.json +14 -0
  28. package/templates/targets/code-apps/vite.config.patch.ts +15 -0
  29. package/templates/targets/power-pages/src/App.patch.tsx +1 -1
  30. package/templates/targets/webresource/README.md +21 -7
  31. package/templates/ui/kendo/src/main.patch.tsx +1 -4
  32. package/templates/ui/shadcn-ui/components.json +1 -1
  33. package/templates/ui/shadcn-ui/src/index.patch.css +9 -26
  34. package/templates/base/src/runtime/EcAppShell.tsx +0 -29
package/dist/pcf.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import path, { dirname } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import fs from "fs-extra";
4
+ import { PCF_SCOPED_CSS_FILE, scopeCssForPcf, } from "./cssScope.js";
4
5
  import { applyLayer, replaceTokensRecursively } from "./libFunctions.js";
6
+ import { ensurePortalContainerRuntime, localizeShadcnPortals, } from "./portalContainers.js";
5
7
  const __filename = fileURLToPath(import.meta.url);
6
8
  const __dirname = dirname(__filename);
7
9
  const RUNTIME_TYPES_TEMPLATE = `export interface PcfWebApi {
@@ -20,38 +22,6 @@ export interface PcfRuntimeContext {
20
22
  \twebApi: PcfWebApi;
21
23
  }
22
24
  `;
23
- function createAppShellTemplate(appId) {
24
- return `import * as React from "react";
25
-
26
- export const EC_APP_SCOPE_CLASS = "ec-app";
27
- export const EC_APP_ID = ${JSON.stringify(appId)};
28
- export const EC_PCF_SCOPE_CLASS = "ec-pcf-shell-control";
29
-
30
- const EcPortalContainerContext = React.createContext<HTMLElement | null>(null);
31
-
32
- export function useEcPortalContainer() {
33
- \treturn React.useContext(EcPortalContainerContext);
34
- }
35
-
36
- export function EcAppShell({ children }: { children: React.ReactNode }) {
37
- \tconst [portalContainer, setPortalContainer] =
38
- \t\tReact.useState<HTMLDivElement | null>(null);
39
-
40
- \treturn (
41
- \t\t<div
42
- \t\t\tclassName={EC_APP_SCOPE_CLASS}
43
- \t\t\tdata-ec-app-id={EC_APP_ID}
44
- \t\t\tdata-ec-app-root=""
45
- \t\t>
46
- \t\t\t<EcPortalContainerContext.Provider value={portalContainer}>
47
- \t\t\t\t{children}
48
- \t\t\t\t<div data-ec-portal-root="" ref={setPortalContainer} />
49
- \t\t\t</EcPortalContainerContext.Provider>
50
- \t\t</div>
51
- \t);
52
- }
53
- `;
54
- }
55
25
  export async function generatePcfFromExistingWebresource(options) {
56
26
  const projectDir = path.resolve(process.cwd(), options.pcfDir);
57
27
  const packageJson = await readJson(path.join(projectDir, "package.json"));
@@ -72,19 +42,24 @@ export async function generatePcfFromExistingWebresource(options) {
72
42
  const packageNameToken = options.packageName ?? toKebabCase(constructorName);
73
43
  const relToProject = toPosixPath(path.relative(outputDir, projectDir) || ".");
74
44
  const appImportPath = ensureRelativeImport(toPosixPath(path.relative(outputDir, path.join(projectDir, "src", "App"))));
75
- const appShellImportPath = ensureRelativeImport(toPosixPath(path.relative(outputDir, path.join(projectDir, "src", "runtime", "EcAppShell"))));
45
+ const portalContainerImportPath = ensureRelativeImport(toPosixPath(path.relative(path.join(outputDir, "runtime"), path.join(projectDir, "src", "runtime", "PortalContainer"))));
76
46
  const runtimeTypesImportPath = ensureRelativeImport(toPosixPath(path.relative(outputDir, path.join(projectDir, "src", "runtime", "types"))));
77
- const cssImportPath = ensureRelativeImport(toPosixPath(path.relative(outputDir, path.join(projectDir, distDirName, "main.css"))));
47
+ const sourceCssPath = path.join(projectDir, distDirName, "main.css");
48
+ const cssImportPath = ensureRelativeImport(PCF_SCOPED_CSS_FILE);
78
49
  await assertFileExists(path.join(projectDir, "src", "App.tsx"), `Could not find src/App.tsx in ${projectDir}.`);
79
- await assertFileExists(path.join(projectDir, distDirName, "main.css"), `Could not find ${distDirName}/main.css in ${projectDir}. Run the webresource build first.`);
50
+ await assertFileExists(sourceCssPath, `Could not find ${distDirName}/main.css in ${projectDir}. Run the webresource build first.`);
80
51
  await ensureRuntimeTypes(projectDir);
81
- await ensureAppShell(projectDir, packageName);
82
- await warnIfCssAppearsUnsafe(path.join(projectDir, distDirName, "main.css"));
52
+ await ensurePortalContainerRuntime(projectDir);
53
+ await localizeShadcnPortals(projectDir, {
54
+ includeGeneratedCompatibility: false,
55
+ });
56
+ const pcfCss = scopeCssForPcf(await fs.readFile(sourceCssPath, "utf8"), constructorName);
83
57
  await fs.remove(outputDir);
84
58
  await applyLayer(templateDir, outputDir);
85
59
  for (const layerDir of layerDirs) {
86
60
  await applyLayer(layerDir, outputDir);
87
61
  }
62
+ await fs.writeFile(path.join(outputDir, PCF_SCOPED_CSS_FILE), pcfCss, "utf8");
88
63
  await replaceTokensRecursively(outputDir, {
89
64
  CONTROL_DESCRIPTION: controlDescription,
90
65
  CONTROL_DISPLAY_NAME: controlDisplayName,
@@ -94,7 +69,7 @@ export async function generatePcfFromExistingWebresource(options) {
94
69
  PCF_VERSION: version,
95
70
  PROJECT_APP_IMPORT: appImportPath,
96
71
  PROJECT_CSS_IMPORT: cssImportPath,
97
- PROJECT_EC_APP_SHELL_IMPORT: appShellImportPath,
72
+ PROJECT_PORTAL_CONTAINER_IMPORT: portalContainerImportPath,
98
73
  PROJECT_NODE_MODULES_TYPES_ROOT: `${relToProject}/node_modules/@types`,
99
74
  PROJECT_REACT_ALIAS: `${relToProject}/node_modules/react`,
100
75
  PROJECT_REACT_DOM_ALIAS: `${relToProject}/node_modules/react-dom`,
@@ -118,52 +93,6 @@ async function ensureRuntimeTypes(projectDir) {
118
93
  await fs.ensureDir(path.dirname(runtimeTypesPath));
119
94
  await fs.writeFile(runtimeTypesPath, RUNTIME_TYPES_TEMPLATE, "utf8");
120
95
  }
121
- async function ensureAppShell(projectDir, appId) {
122
- const appShellPath = path.join(projectDir, "src", "runtime", "EcAppShell.tsx");
123
- if (await fs.pathExists(appShellPath)) {
124
- return;
125
- }
126
- await fs.ensureDir(path.dirname(appShellPath));
127
- await fs.writeFile(appShellPath, createAppShellTemplate(appId), "utf8");
128
- }
129
- async function warnIfCssAppearsUnsafe(cssPath) {
130
- const css = await fs.readFile(cssPath, "utf8");
131
- const unsafeChecks = [
132
- { name: "global body rule", pattern: /(^|})\s*body\s*\{/ },
133
- {
134
- name: "global shadcn :root token rule",
135
- pattern: /(^|})\s*:root\s*\{[^}]*--background\s*:/,
136
- },
137
- {
138
- name: "global shadcn dark token rule",
139
- pattern: /(^|})\s*\.dark\s*\{[^}]*--background\s*:/,
140
- },
141
- {
142
- name: "Tailwind Preflight universal reset",
143
- pattern: /(^|})\s*\*,\s*::before,\s*::after,\s*::backdrop\b/,
144
- },
145
- { name: "unprefixed flex utility", pattern: /(^|})\s*\.flex\s*\{/ },
146
- { name: "unprefixed grid utility", pattern: /(^|})\s*\.grid\s*\{/ },
147
- { name: "unprefixed hidden utility", pattern: /(^|})\s*\.hidden\s*\{/ },
148
- { name: "unprefixed border utility", pattern: /(^|})\s*\.border\s*\{/ },
149
- { name: "unprefixed text-sm utility", pattern: /(^|})\s*\.text-sm\s*\{/ },
150
- {
151
- name: "unprefixed bg-background utility",
152
- pattern: /(^|})\s*\.bg-background\s*\{/,
153
- },
154
- ];
155
- const matches = unsafeChecks
156
- .filter((check) => check.pattern.test(css))
157
- .map((check) => check.name);
158
- if (matches.length === 0) {
159
- return;
160
- }
161
- console.warn([
162
- "Warning: the built CSS appears to use unscoped Tailwind/shadcn styles and may leak into the host page when deployed as PCF.",
163
- `Detected: ${matches.join(", ")}.`,
164
- "Regenerate or migrate the source app to the scoped CSS template before deploying to Dynamics 365.",
165
- ].join("\n"));
166
- }
167
96
  async function readJson(filePath) {
168
97
  try {
169
98
  return await fs.readJson(filePath);
package/dist/pcf.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pcf.js","sourceRoot":"","sources":["../src/pcf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pcf.js","sourceRoot":"","sources":["../src/pcf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function ensurePortalContainerRuntime(projectDir: string): Promise<void>;
2
+ interface LocalizeShadcnPortalsOptions {
3
+ includeGeneratedCompatibility?: boolean;
4
+ }
5
+ export declare function localizeShadcnPortals(projectDir: string, options?: LocalizeShadcnPortalsOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=portalContainers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portalContainers.d.ts","sourceRoot":"","sources":["../src/portalContainers.ts"],"names":[],"mappings":"AAaA,wBAAsB,4BAA4B,CACjD,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,UAAU,4BAA4B;IACrC,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,wBAAsB,qBAAqB,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,4BAAiC,GACxC,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
@@ -0,0 +1,118 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ const PORTAL_CONTAINER_TEMPLATE = `import * as React from "react";
4
+
5
+ export const PortalContainerContext =
6
+ \tReact.createContext<HTMLElement | null>(null);
7
+
8
+ export function usePortalContainer() {
9
+ \treturn React.useContext(PortalContainerContext);
10
+ }
11
+ `;
12
+ export async function ensurePortalContainerRuntime(projectDir) {
13
+ const portalContainerPath = path.join(projectDir, "src", "runtime", "PortalContainer.ts");
14
+ if (await fs.pathExists(portalContainerPath)) {
15
+ return;
16
+ }
17
+ await fs.ensureDir(path.dirname(portalContainerPath));
18
+ await fs.writeFile(portalContainerPath, PORTAL_CONTAINER_TEMPLATE, "utf8");
19
+ }
20
+ export async function localizeShadcnPortals(projectDir, options = {}) {
21
+ const componentsDir = path.join(projectDir, "src", "components", "ui");
22
+ if (!(await fs.pathExists(componentsDir))) {
23
+ return;
24
+ }
25
+ await ensurePortalContainerRuntime(projectDir);
26
+ const entries = await fs.readdir(componentsDir, { withFileTypes: true });
27
+ for (const entry of entries) {
28
+ if (!entry.isFile() || !entry.name.endsWith(".tsx")) {
29
+ continue;
30
+ }
31
+ const filePath = path.join(componentsDir, entry.name);
32
+ const source = await fs.readFile(filePath, "utf8");
33
+ const withPortalContainers = withPortalRuntime(source, filePath);
34
+ const updated = options.includeGeneratedCompatibility === false
35
+ ? withPortalContainers
36
+ : withGeneratedShadcnCompatibility(withPortalContainers);
37
+ if (updated !== source) {
38
+ await fs.writeFile(filePath, updated, "utf8");
39
+ }
40
+ }
41
+ }
42
+ function withPortalRuntime(source, filePath) {
43
+ const migratedSource = source.replace(/from ["']@\/runtime\/EcAppShell["']/g, 'from "@/runtime/PortalContainer"');
44
+ const withGenericPortalRuntime = migratedSource
45
+ .replace(/from ["']@\/runtime\/EcPortalContainer["']/g, 'from "@/runtime/PortalContainer"')
46
+ .replace(/\buseEcPortalContainer\b/g, "usePortalContainer");
47
+ const withPortalContainers = withGenericPortalRuntime.replace(/<([A-Za-z][A-Za-z0-9]*Primitive)\.Portal\b(?![^>]*\bcontainer=)/g, "<$1.Portal container={portalContainer ?? undefined}");
48
+ if (withPortalContainers === withGenericPortalRuntime) {
49
+ return withGenericPortalRuntime;
50
+ }
51
+ return addPortalImport(addPortalHookDeclarations(withPortalContainers, filePath));
52
+ }
53
+ function withGeneratedShadcnCompatibility(source) {
54
+ const withChartAttributeSelectors = removeStandaloneClassPrefixes(source)
55
+ .replace(/\[stroke=#ccc\]/g, "[stroke='#ccc']")
56
+ .replace(/\[stroke=#fff\]/g, "[stroke='#fff']");
57
+ if (!withChartAttributeSelectors.includes('from "react-day-picker"')) {
58
+ return withChartAttributeSelectors;
59
+ }
60
+ return withChartAttributeSelectors.replace(/(\n\s*)table:/g, "$1month_grid:");
61
+ }
62
+ function removeStandaloneClassPrefixes(source) {
63
+ let updated = source;
64
+ let previous;
65
+ do {
66
+ previous = updated;
67
+ updated = updated.replace(/(^|[\s"'])ec:(?=\s|["'])/g, "$1");
68
+ } while (updated !== previous);
69
+ return updated;
70
+ }
71
+ function addPortalImport(source) {
72
+ if (source.includes('from "@/runtime/PortalContainer"')) {
73
+ return source;
74
+ }
75
+ const importPattern = /import[\s\S]*?from\s+["'][^"']+["'];?\n|import\s+["'][^"']+["'];?\n/g;
76
+ let insertAt = 0;
77
+ for (const match of source.matchAll(importPattern)) {
78
+ insertAt = (match.index ?? 0) + match[0].length;
79
+ }
80
+ const importLine = 'import { usePortalContainer } from "@/runtime/PortalContainer"\n';
81
+ return `${source.slice(0, insertAt)}${importLine}${source.slice(insertAt)}`;
82
+ }
83
+ function addPortalHookDeclarations(source, filePath) {
84
+ const lines = source.split("\n");
85
+ const hookBodyLines = new Set();
86
+ for (let index = 0; index < lines.length; index += 1) {
87
+ if (!lines[index]?.includes("container={portalContainer ?? undefined}")) {
88
+ continue;
89
+ }
90
+ const bodyLine = findContainingFunctionBodyLine(lines, index);
91
+ if (bodyLine === undefined) {
92
+ throw new Error(`Could not locate a function body for a shadcn Portal in ${filePath}.`);
93
+ }
94
+ hookBodyLines.add(bodyLine);
95
+ }
96
+ for (const bodyLine of [...hookBodyLines].sort((a, b) => b - a)) {
97
+ if (lines[bodyLine + 1]?.includes("const portalContainer")) {
98
+ continue;
99
+ }
100
+ const indent = lines[bodyLine]?.match(/^(\s*)/)?.[1] ?? "";
101
+ lines.splice(bodyLine + 1, 0, `${indent} const portalContainer = usePortalContainer()`);
102
+ }
103
+ return lines.join("\n");
104
+ }
105
+ function findContainingFunctionBodyLine(lines, portalLine) {
106
+ for (let index = portalLine; index >= 0; index -= 1) {
107
+ if (!/^\s*function\s+\w+/.test(lines[index] ?? "")) {
108
+ continue;
109
+ }
110
+ for (let bodyLine = index; bodyLine <= portalLine; bodyLine += 1) {
111
+ if (/\)\s*\{\s*$/.test(lines[bodyLine] ?? "")) {
112
+ return bodyLine;
113
+ }
114
+ }
115
+ }
116
+ return undefined;
117
+ }
118
+ //# sourceMappingURL=portalContainers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portalContainers.js","sourceRoot":"","sources":["../src/portalContainers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-ec-app",
3
- "version": "1.5.0",
4
- "description": "Unified CLI tool to create different types of EC applications: Webresource, Portal, Power Pages",
3
+ "version": "1.7.0",
4
+ "description": "Unified CLI tool to create different types of EC applications: Webresource, Portal, Power Pages, Power Apps Code Apps",
5
5
  "bin": {
6
6
  "create-ec-app": "./dist/index.js"
7
7
  },
@@ -20,6 +20,8 @@
20
20
  "tailwind",
21
21
  "cli",
22
22
  "powerpages",
23
+ "code-apps",
24
+ "power-apps-code-apps",
23
25
  "portal",
24
26
  "webresource",
25
27
  "dynamics",
@@ -38,7 +40,8 @@
38
40
  "type": "module",
39
41
  "dependencies": {
40
42
  "@clack/prompts": "^0.11.0",
41
- "fs-extra": "^11.3.2"
43
+ "fs-extra": "^11.3.2",
44
+ "postcss": "^8.5.15"
42
45
  },
43
46
  "devDependencies": {
44
47
  "@semantic-release/commit-analyzer": "^13.0.1",
@@ -2,60 +2,121 @@
2
2
 
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
+ import postcss from "postcss";
5
6
 
6
- const appPath = process.argv[2];
7
+ const pcfPath = process.argv[2];
7
8
 
8
- if (!appPath) {
9
- console.error("Usage: node scripts/check-generated-css-scope.mjs <generated-app>");
9
+ if (!pcfPath) {
10
+ console.error("Usage: node scripts/check-generated-css-scope.mjs <generated-pcf-control>");
10
11
  process.exit(1);
11
12
  }
12
13
 
13
- const cssPath = path.join(path.resolve(appPath), "dist", "main.css");
14
+ const cssPath = path.join(path.resolve(pcfPath), "pcf-scoped.css");
14
15
 
15
16
  if (!fs.existsSync(cssPath)) {
16
- console.error(`Could not find ${cssPath}. Build the generated app first.`);
17
+ console.error(`Could not find ${cssPath}. Generate the PCF control first.`);
17
18
  process.exit(1);
18
19
  }
19
20
 
20
21
  const css = fs.readFileSync(cssPath, "utf8");
22
+ const root = postcss.parse(css, { from: cssPath });
23
+ const failures = [];
21
24
 
22
- const forbidden = [
23
- { name: "global body rule", pattern: /(^|})\s*body\s*\{/ },
24
- {
25
- name: "global shadcn :root token rule",
26
- pattern: /(^|})\s*:root\s*\{[^}]*--background\s*:/,
27
- },
28
- {
29
- name: "global shadcn dark token rule",
30
- pattern: /(^|})\s*\.dark\s*\{[^}]*--background\s*:/,
31
- },
32
- { name: "unprefixed flex utility", pattern: /(^|})\s*\.flex\s*\{/ },
33
- { name: "unprefixed grid utility", pattern: /(^|})\s*\.grid\s*\{/ },
34
- { name: "unprefixed hidden utility", pattern: /(^|})\s*\.hidden\s*\{/ },
35
- {
36
- name: "unprefixed bg-background utility",
37
- pattern: /(^|})\s*\.bg-background\s*\{/,
38
- },
39
- ];
40
-
41
- const required = [
42
- { name: "ec app scope", pattern: /\.ec-app\b/ },
43
- { name: "prefixed flex utility", pattern: /\.ec\\:flex\b/ },
44
- ];
45
-
46
- const failures = [
47
- ...forbidden.filter((check) => check.pattern.test(css)).map((check) => check.name),
48
- ...required
49
- .filter((check) => !check.pattern.test(css))
50
- .map((check) => `missing ${check.name}`),
51
- ];
25
+ root.walkRules((rule) => {
26
+ if (isKeyframesRule(rule)) {
27
+ return;
28
+ }
29
+
30
+ for (const selector of splitSelectorList(rule.selector)) {
31
+ if (!selector.includes(".pcf-shell-control")) {
32
+ failures.push(`unscoped PCF rule: ${selector}`);
33
+ }
34
+ }
35
+ });
36
+
37
+ if (!css.includes("[data-pcf-control=")) {
38
+ failures.push("missing PCF control data attribute scope");
39
+ }
40
+
41
+ if (css.includes(".ec-pcf-shell-control") || css.includes("[data-ec-pcf-control=")) {
42
+ failures.push("legacy EC PCF scope is still present");
43
+ }
52
44
 
53
45
  if (failures.length > 0) {
54
- console.error("CSS scope check failed:");
46
+ console.error("PCF CSS scope check failed:");
55
47
  for (const failure of failures) {
56
48
  console.error(`- ${failure}`);
57
49
  }
58
50
  process.exit(1);
59
51
  }
60
52
 
61
- console.log("CSS scope check passed.");
53
+ console.log("PCF CSS scope check passed.");
54
+
55
+ function isKeyframesRule(rule) {
56
+ let parent = rule.parent;
57
+
58
+ while (parent) {
59
+ if (
60
+ parent.type === "atrule" &&
61
+ parent.name.toLowerCase().endsWith("keyframes")
62
+ ) {
63
+ return true;
64
+ }
65
+
66
+ parent = parent.parent;
67
+ }
68
+
69
+ return false;
70
+ }
71
+
72
+ function splitSelectorList(selectorList) {
73
+ const selectors = [];
74
+ let start = 0;
75
+ let nesting = 0;
76
+ let quote = null;
77
+ let escaped = false;
78
+
79
+ for (let index = 0; index < selectorList.length; index += 1) {
80
+ const char = selectorList[index];
81
+
82
+ if (escaped) {
83
+ escaped = false;
84
+ continue;
85
+ }
86
+
87
+ if (char === "\\") {
88
+ escaped = true;
89
+ continue;
90
+ }
91
+
92
+ if (quote) {
93
+ if (char === quote) {
94
+ quote = null;
95
+ }
96
+ continue;
97
+ }
98
+
99
+ if (char === '"' || char === "'") {
100
+ quote = char;
101
+ continue;
102
+ }
103
+
104
+ if (char === "(" || char === "[") {
105
+ nesting += 1;
106
+ continue;
107
+ }
108
+
109
+ if (char === ")" || char === "]") {
110
+ nesting = Math.max(0, nesting - 1);
111
+ continue;
112
+ }
113
+
114
+ if (char === "," && nesting === 0) {
115
+ selectors.push(selectorList.slice(start, index).trim());
116
+ start = index + 1;
117
+ }
118
+ }
119
+
120
+ selectors.push(selectorList.slice(start).trim());
121
+ return selectors.filter(Boolean);
122
+ }
@@ -2,7 +2,7 @@ import "./App.css";
2
2
 
3
3
  function App() {
4
4
  return (
5
- <div className="ec:flex ec:flex-col ec:h-screen ec:items-center ec:justify-center">
5
+ <div className="flex h-screen flex-col items-center justify-center">
6
6
  <p>Hello, world!</p>
7
7
  </div>
8
8
  );
@@ -1,6 +1,3 @@
1
- @layer theme, base, components, utilities;
2
-
3
- @import "tailwindcss/theme.css" layer(theme) prefix(ec);
4
- @import "tailwindcss/utilities.css" layer(utilities) prefix(ec);
1
+ @import "tailwindcss";
5
2
 
6
3
  @custom-variant hover (&:hover);
@@ -4,7 +4,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
 
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
  );
@@ -9,6 +9,29 @@ npm install
9
9
  npm run build
10
10
  ```
11
11
 
12
+ ## Regenerate From Webresource Changes
13
+
14
+ Do not edit this generated PCF folder as the durable source of truth. From the webresource root, rebuild and regenerate:
15
+
16
+ ```bash
17
+ npm run build
18
+ npx create-ec-app@latest \
19
+ --pcf-dir . \
20
+ --output ./pcf/{{PCF_CONSTRUCTOR}} \
21
+ --namespace {{PCF_NAMESPACE}} \
22
+ --constructor {{PCF_CONSTRUCTOR}} \
23
+ --display-name "{{CONTROL_DISPLAY_NAME}}"
24
+ cd pcf/{{PCF_CONSTRUCTOR}}
25
+ npm install
26
+ npm run build
27
+ ```
28
+
29
+ Then run the harness:
30
+
31
+ ```bash
32
+ npm run start -- --no-open
33
+ ```
34
+
12
35
  ## Control Info
13
36
 
14
37
  - Namespace: `{{PCF_NAMESPACE}}`
@@ -18,19 +41,15 @@ npm run build
18
41
 
19
42
  ## Notes
20
43
 
21
- - The wrapper imports `src/App` directly and reuses the built `dist/main.css`.
44
+ - The wrapper imports `src/App` directly, renders it through the local PCF shell, and uses generated `pcf-scoped.css` derived from the webresource build.
45
+ - Source-app CSS imports are ignored by the PCF webpack config because the built CSS is already included through `pcf-scoped.css`.
22
46
  - Regenerate this folder after rebuilding the webresource whenever the app changes.
23
47
  - The project includes both `pcf-scripts` build support and a `.pcfproj` for Dataverse solution packaging flows.
24
48
 
25
49
  ## CSS scoping
26
50
 
27
- This PCF control renders the app inside `.ec-app` and keeps the PCF host container class `.ec-pcf-shell-control`.
28
-
29
- Generated Tailwind/shadcn styles are scoped for embeddability:
51
+ This PCF control renders the app inside the PCF host container class `.pcf-shell-control`.
30
52
 
31
- - Tailwind Preflight is not imported globally.
32
- - Tailwind utilities use the `ec:` prefix.
33
- - shadcn theme variables are defined under `.ec-app`.
34
- - Radix/shadcn portals render into the app-local portal root where supported.
53
+ During PCF generation, `create-ec-app` reads the webresource's built `dist/main.css`, scopes CSS selectors to this control's `.pcf-shell-control[data-pcf-control="{{PCF_CONSTRUCTOR}}"]` host selector, and writes the result to `pcf-scoped.css`.
35
54
 
36
- If this PCF was generated from an older app whose `dist/main.css` contains global Tailwind/shadcn styles, those styles may still leak into the model-driven app form. Regenerate or migrate the source app to the scoped CSS template.
55
+ Tailwind utilities and base selectors are isolated under the generated PCF host selector. Radix/shadcn portals render into the PCF-local portal root where supported.