@bravostudioai/react 0.1.15 → 0.1.17
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/dist/cli/commands/generate.js +90 -85
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/codegen/generator.js +218 -212
- package/dist/codegen/generator.js.map +1 -1
- package/dist/components/EncoreApp.js +34 -22
- package/dist/components/EncoreApp.js.map +1 -1
- package/dist/src/cli/commands/generate.d.ts.map +1 -1
- package/dist/src/codegen/generator.d.ts +5 -1
- package/dist/src/codegen/generator.d.ts.map +1 -1
- package/dist/src/components/EncoreApp.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/generate.ts +35 -3
- package/src/codegen/generator.ts +12 -1
- package/src/components/EncoreApp.tsx +9 -1
- package/src/lib/fetcher.ts +85 -87
|
@@ -85,12 +85,14 @@ async function generateWrapper({
|
|
|
85
85
|
outputPath,
|
|
86
86
|
cachedAppData,
|
|
87
87
|
isProduction,
|
|
88
|
+
usedNames,
|
|
88
89
|
}: {
|
|
89
90
|
appId: string;
|
|
90
91
|
pageId: string;
|
|
91
92
|
outputPath: string;
|
|
92
93
|
cachedAppData?: any;
|
|
93
94
|
isProduction?: boolean;
|
|
95
|
+
usedNames?: Set<string>;
|
|
94
96
|
}) {
|
|
95
97
|
console.log(`Generating wrapper for app: ${appId}, page: ${pageId}`);
|
|
96
98
|
|
|
@@ -249,7 +251,27 @@ async function generateWrapper({
|
|
|
249
251
|
pageName = pageData.name || pageData.id || pageName;
|
|
250
252
|
|
|
251
253
|
// Generate component name and directory name
|
|
252
|
-
|
|
254
|
+
let { directoryPath, componentName } = generateNames(appName, pageName);
|
|
255
|
+
|
|
256
|
+
// Ensure unique component name
|
|
257
|
+
if (usedNames) {
|
|
258
|
+
let uniqueName = componentName;
|
|
259
|
+
let counter = 2;
|
|
260
|
+
while (usedNames.has(uniqueName)) {
|
|
261
|
+
uniqueName = `${componentName}${counter}`;
|
|
262
|
+
counter++;
|
|
263
|
+
}
|
|
264
|
+
if (uniqueName !== componentName) {
|
|
265
|
+
componentName = uniqueName;
|
|
266
|
+
// Reconstitute directory path with new component name
|
|
267
|
+
// generateNames uses: join(appPascal, pagePascal) where pagePascal is componentName
|
|
268
|
+
const appCamel = sanitizePropName(appName);
|
|
269
|
+
const appPascal = appCamel.charAt(0).toUpperCase() + appCamel.slice(1);
|
|
270
|
+
directoryPath = join(appPascal, componentName);
|
|
271
|
+
}
|
|
272
|
+
usedNames.add(componentName);
|
|
273
|
+
}
|
|
274
|
+
|
|
253
275
|
const directoryName = directoryPath;
|
|
254
276
|
|
|
255
277
|
// Update output path
|
|
@@ -271,7 +293,15 @@ async function generateWrapper({
|
|
|
271
293
|
forms,
|
|
272
294
|
selectInputs,
|
|
273
295
|
actionButtons,
|
|
274
|
-
!!isProduction
|
|
296
|
+
!!isProduction,
|
|
297
|
+
{
|
|
298
|
+
width: pageData.style?.width,
|
|
299
|
+
height: pageData.style?.height,
|
|
300
|
+
aspectRatio:
|
|
301
|
+
pageData.style?.width && pageData.style?.height
|
|
302
|
+
? pageData.style.width / pageData.style.height
|
|
303
|
+
: undefined,
|
|
304
|
+
}
|
|
275
305
|
);
|
|
276
306
|
|
|
277
307
|
const readmeContent = generateReadme(
|
|
@@ -373,6 +403,7 @@ export async function runGenerate(args: string[]) {
|
|
|
373
403
|
return;
|
|
374
404
|
}
|
|
375
405
|
|
|
406
|
+
const usedNames = new Set<string>();
|
|
376
407
|
for (const page of pages) {
|
|
377
408
|
if (!page.id) continue;
|
|
378
409
|
try {
|
|
@@ -382,6 +413,7 @@ export async function runGenerate(args: string[]) {
|
|
|
382
413
|
outputPath,
|
|
383
414
|
cachedAppData: appData,
|
|
384
415
|
isProduction,
|
|
416
|
+
usedNames,
|
|
385
417
|
});
|
|
386
418
|
} catch (error) {
|
|
387
419
|
console.warn(
|
|
@@ -400,7 +432,7 @@ export async function runGenerate(args: string[]) {
|
|
|
400
432
|
const [appId, pageId, outputPath] = cleanArgs;
|
|
401
433
|
|
|
402
434
|
try {
|
|
403
|
-
await generateWrapper({ appId, pageId, outputPath, isProduction });
|
|
435
|
+
await generateWrapper({ appId, pageId, outputPath, isProduction }); // Single page generation, no collision context needed unless we wanted global uniqueness but usually used for one-off
|
|
404
436
|
} catch (error) {
|
|
405
437
|
console.error("\nError:", error instanceof Error ? error.message : error);
|
|
406
438
|
process.exit(1);
|
package/src/codegen/generator.ts
CHANGED
|
@@ -28,7 +28,12 @@ export function generateComponentCode(
|
|
|
28
28
|
forms: FormInfo[],
|
|
29
29
|
selectInputs: SelectInputInfo[],
|
|
30
30
|
actionButtons: ActionButtonInfo[],
|
|
31
|
-
isProduction: boolean = false
|
|
31
|
+
isProduction: boolean = false,
|
|
32
|
+
pageMeta?: {
|
|
33
|
+
width?: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
aspectRatio?: number;
|
|
36
|
+
}
|
|
32
37
|
): string {
|
|
33
38
|
// Generate prop types
|
|
34
39
|
const propTypes: string[] = [];
|
|
@@ -439,6 +444,12 @@ ${dataMapping.join("\n")}
|
|
|
439
444
|
}
|
|
440
445
|
|
|
441
446
|
export default ${componentName};
|
|
447
|
+
|
|
448
|
+
export const PageMeta = {
|
|
449
|
+
width: ${pageMeta?.width ?? "undefined"},
|
|
450
|
+
height: ${pageMeta?.height ?? "undefined"},
|
|
451
|
+
aspectRatio: ${pageMeta?.aspectRatio ?? "undefined"},
|
|
452
|
+
};
|
|
442
453
|
`;
|
|
443
454
|
}
|
|
444
455
|
|
|
@@ -828,7 +828,15 @@ const EncoreApp = ({
|
|
|
828
828
|
overflow: "hidden",
|
|
829
829
|
}}
|
|
830
830
|
>
|
|
831
|
-
<div
|
|
831
|
+
<div
|
|
832
|
+
ref={contentWrapperRef}
|
|
833
|
+
style={{
|
|
834
|
+
width: "100%",
|
|
835
|
+
height: "100%",
|
|
836
|
+
display: "flex",
|
|
837
|
+
flexDirection: "column",
|
|
838
|
+
}}
|
|
839
|
+
>
|
|
832
840
|
<Suspense fallback={fallback || <div />}>
|
|
833
841
|
<EncoreComponentIdContext.Provider value={{ componentId }}>
|
|
834
842
|
<EncoreActionContext.Provider value={{ onAction }}>
|
package/src/lib/fetcher.ts
CHANGED
|
@@ -4,104 +4,102 @@ import useEncoreState from "../stores/useEncoreState";
|
|
|
4
4
|
|
|
5
5
|
// Get baseURL from store at runtime instead of build time
|
|
6
6
|
const getAppsServiceUrl = () => {
|
|
7
|
-
|
|
7
|
+
return useEncoreState.getState().baseURL;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
const bundledResponses: Record<string, string> = {};
|
|
11
11
|
|
|
12
12
|
const fetcher = (url: string) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// try next
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
throw new Error(
|
|
38
|
-
`Local app.json not found for ${appId} (tried ${tryUrls.join(
|
|
39
|
-
", ",
|
|
40
|
-
)})`,
|
|
41
|
-
);
|
|
42
|
-
})();
|
|
13
|
+
// Local mode: map Encore service URLs to files in /flex-layout
|
|
14
|
+
const forceLocal = /\buseLocal=1\b/.test(url);
|
|
15
|
+
if (forceLocal || isLocalMode()) {
|
|
16
|
+
const pathOnly = url.split("?")[0];
|
|
17
|
+
const absBase = null;
|
|
18
|
+
// /devices/apps/:appId
|
|
19
|
+
const appMatch = pathOnly.match(/^\/devices\/apps\/([^/]+)$/);
|
|
20
|
+
if (appMatch) {
|
|
21
|
+
const appId = appMatch[1];
|
|
22
|
+
const tryUrls = [
|
|
23
|
+
`/flex-layout/${appId}/${appId}.json`,
|
|
24
|
+
absBase ? `/@fs/${absBase}/${appId}/${appId}.json` : undefined,
|
|
25
|
+
].filter(Boolean) as string[];
|
|
26
|
+
return (async () => {
|
|
27
|
+
for (const u of tryUrls) {
|
|
28
|
+
try {
|
|
29
|
+
const r = await fetch(u);
|
|
30
|
+
if (r.ok) return r.json();
|
|
31
|
+
} catch {
|
|
32
|
+
// try next
|
|
33
|
+
}
|
|
43
34
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/^\/devices\/apps\/([^/]+)\/node\/([^/?#]+)$/,
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Local app.json not found for ${appId} (tried ${tryUrls.join(", ")})`
|
|
47
37
|
);
|
|
48
|
-
|
|
49
|
-
const appId = pageMatch[1];
|
|
50
|
-
const pageId = pageMatch[2];
|
|
51
|
-
// Try a dedicated page JSON first (if present)
|
|
52
|
-
const tryUrls = [
|
|
53
|
-
`/flex-layout/${appId}/${pageId}.json`,
|
|
54
|
-
absBase
|
|
55
|
-
? `/@fs/${absBase}/${appId}/${pageId}.json`
|
|
56
|
-
: undefined,
|
|
57
|
-
].filter(Boolean) as string[];
|
|
58
|
-
return fetch(tryUrls[0])
|
|
59
|
-
.then(async (r) => {
|
|
60
|
-
if (r.ok) return r.json();
|
|
61
|
-
// try secondary url if defined
|
|
62
|
-
if (tryUrls[1]) {
|
|
63
|
-
const r2 = await fetch(tryUrls[1]).catch(() => null);
|
|
64
|
-
if (r2 && r2.ok) return r2.json();
|
|
65
|
-
}
|
|
66
|
-
// Fallback: derive minimal shape from app.json
|
|
67
|
-
const appJson =
|
|
68
|
-
(await fetch(`/flex-layout/${appId}/${appId}.json`)
|
|
69
|
-
.then((a) => (a.ok ? a.json() : null))
|
|
70
|
-
.catch(() => null)) ||
|
|
71
|
-
(absBase
|
|
72
|
-
? await fetch(
|
|
73
|
-
`/@fs/${absBase}/${appId}/${appId}.json`,
|
|
74
|
-
)
|
|
75
|
-
.then((a) => (a.ok ? a.json() : null))
|
|
76
|
-
.catch(() => null)
|
|
77
|
-
: null);
|
|
78
|
-
// Keep shape compatible with current consumers
|
|
79
|
-
const pages =
|
|
80
|
-
appJson?.app?.data?.pages ||
|
|
81
|
-
appJson?.data?.pages ||
|
|
82
|
-
[];
|
|
83
|
-
const page = pages.find((p: any) => p?.id === pageId) || {};
|
|
84
|
-
return { clientData: page };
|
|
85
|
-
})
|
|
86
|
-
.catch(async () => {
|
|
87
|
-
// Final fallback: minimal object
|
|
88
|
-
return { clientData: null };
|
|
89
|
-
});
|
|
90
|
-
}
|
|
38
|
+
})();
|
|
91
39
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
40
|
+
// /devices/apps/:appId/node/:pageId
|
|
41
|
+
const pageMatch = pathOnly.match(
|
|
42
|
+
/^\/devices\/apps\/([^/]+)\/node\/([^/?#]+)$/
|
|
43
|
+
);
|
|
44
|
+
if (pageMatch) {
|
|
45
|
+
const appId = pageMatch[1];
|
|
46
|
+
const pageId = pageMatch[2];
|
|
47
|
+
// Try a dedicated page JSON first (if present)
|
|
48
|
+
const tryUrls = [
|
|
49
|
+
`/flex-layout/${appId}/${pageId}.json`,
|
|
50
|
+
absBase ? `/@fs/${absBase}/${appId}/${pageId}.json` : undefined,
|
|
51
|
+
].filter(Boolean) as string[];
|
|
52
|
+
return fetch(tryUrls[0])
|
|
53
|
+
.then(async (r) => {
|
|
54
|
+
if (r.ok) return r.json();
|
|
55
|
+
// try secondary url if defined
|
|
56
|
+
if (tryUrls[1]) {
|
|
57
|
+
const r2 = await fetch(tryUrls[1]).catch(() => null);
|
|
58
|
+
if (r2 && r2.ok) return r2.json();
|
|
59
|
+
}
|
|
60
|
+
// Fallback: derive minimal shape from app.json
|
|
61
|
+
const appJson =
|
|
62
|
+
(await fetch(`/flex-layout/${appId}/${appId}.json`)
|
|
63
|
+
.then((a) => (a.ok ? a.json() : null))
|
|
64
|
+
.catch(() => null)) ||
|
|
65
|
+
(absBase
|
|
66
|
+
? await fetch(`/@fs/${absBase}/${appId}/${appId}.json`)
|
|
67
|
+
.then((a) => (a.ok ? a.json() : null))
|
|
68
|
+
.catch(() => null)
|
|
69
|
+
: null);
|
|
70
|
+
// Keep shape compatible with current consumers
|
|
71
|
+
const pages = appJson?.app?.data?.pages || appJson?.data?.pages || [];
|
|
72
|
+
const page = pages.find((p: any) => p?.id === pageId) || {};
|
|
73
|
+
return { clientData: page };
|
|
74
|
+
})
|
|
75
|
+
.catch(async () => {
|
|
76
|
+
// Final fallback: minimal object
|
|
77
|
+
return { clientData: null };
|
|
78
|
+
});
|
|
95
79
|
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (bundledResponses?.[url]) {
|
|
83
|
+
return JSON.parse(bundledResponses?.[url]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Get baseURL at runtime from store
|
|
87
|
+
const appsServiceUrl = getAppsServiceUrl();
|
|
96
88
|
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
console.log(
|
|
90
|
+
"[Fetcher] Requesting:",
|
|
91
|
+
url,
|
|
92
|
+
"BaseURL:",
|
|
93
|
+
appsServiceUrl,
|
|
94
|
+
"Headers:",
|
|
95
|
+
{ "x-app-clientrendered": "disabled" }
|
|
96
|
+
);
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
return axios({
|
|
99
|
+
baseURL: appsServiceUrl,
|
|
100
|
+
url,
|
|
101
|
+
// headers: { "x-app-clientrendered": "true" },
|
|
102
|
+
}).then((res) => res.data);
|
|
105
103
|
};
|
|
106
104
|
|
|
107
105
|
export default fetcher;
|