@slicemachine/manager 0.24.4-alpha.xru-slice-generation-ai-poc.1 → 0.24.4-alpha.xru-slice-generation-ai-poc.2
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/_node_modules/@amplitude/experiment-node-server/dist/src/local/client.cjs +1 -1
- package/dist/_node_modules/@amplitude/experiment-node-server/dist/src/local/client.js +1 -1
- package/dist/_node_modules/cross-spawn/index.cjs +1 -1
- package/dist/_node_modules/cross-spawn/index.js +1 -1
- package/dist/_node_modules/execa/lib/pipe.cjs +2 -2
- package/dist/_node_modules/execa/lib/pipe.cjs.map +1 -1
- package/dist/_node_modules/execa/lib/stream.cjs +3 -3
- package/dist/_node_modules/execa/lib/stream.cjs.map +1 -1
- package/dist/_virtual/index2.cjs +4 -3
- package/dist/_virtual/index2.cjs.map +1 -1
- package/dist/_virtual/index2.js +4 -2
- package/dist/_virtual/index2.js.map +1 -1
- package/dist/_virtual/index3.cjs +3 -4
- package/dist/_virtual/index3.cjs.map +1 -1
- package/dist/_virtual/index3.js +2 -4
- package/dist/_virtual/index3.js.map +1 -1
- package/dist/managers/project/ProjectManager.cjs +8 -8
- package/dist/managers/project/ProjectManager.cjs.map +1 -1
- package/dist/managers/slices/SlicesManager.cjs +370 -353
- package/dist/managers/slices/SlicesManager.cjs.map +1 -1
- package/dist/managers/slices/SlicesManager.d.ts +1 -1
- package/dist/managers/slices/SlicesManager.js +370 -353
- package/dist/managers/slices/SlicesManager.js.map +1 -1
- package/package.json +2 -2
- package/src/managers/slices/SlicesManager.ts +460 -438
@@ -43,6 +43,7 @@ import { BaseManager } from "../BaseManager";
|
|
43
43
|
import {
|
44
44
|
BedrockRuntimeClient,
|
45
45
|
ConverseCommand,
|
46
|
+
Message,
|
46
47
|
} from "@aws-sdk/client-bedrock-runtime";
|
47
48
|
|
48
49
|
type SlicesManagerReadSliceLibraryReturnType = {
|
@@ -226,7 +227,7 @@ type SliceMachineManagerGenerateSliceReturnType = {
|
|
226
227
|
};
|
227
228
|
|
228
229
|
type SliceMachineManagerGenerateSlicesFromUrlArgs = {
|
229
|
-
|
230
|
+
sliceImages: Uint8Array[];
|
230
231
|
};
|
231
232
|
|
232
233
|
type SliceMachineManagerGenerateSlicesFromUrlReturnType = {
|
@@ -1856,61 +1857,181 @@ type GroupField = {
|
|
1856
1857
|
): Promise<SliceMachineManagerGenerateSlicesFromUrlReturnType> {
|
1857
1858
|
assertPluginsInitialized(this.sliceMachinePluginRunner);
|
1858
1859
|
|
1859
|
-
const { OPENAI_API_KEY } =
|
1860
|
+
const { OPENAI_API_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY } =
|
1861
|
+
process.env;
|
1862
|
+
|
1863
|
+
if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY) {
|
1864
|
+
throw new Error("AWS credentials are not set.");
|
1865
|
+
}
|
1866
|
+
const AWS_REGION = "us-east-1";
|
1867
|
+
const bedrockClient = new BedrockRuntimeClient({
|
1868
|
+
region: AWS_REGION,
|
1869
|
+
credentials: {
|
1870
|
+
accessKeyId: AWS_ACCESS_KEY_ID,
|
1871
|
+
secretAccessKey: AWS_SECRET_ACCESS_KEY,
|
1872
|
+
},
|
1873
|
+
});
|
1874
|
+
|
1860
1875
|
if (!OPENAI_API_KEY) {
|
1861
1876
|
throw new Error("OPENAI_API_KEY is not set.");
|
1862
1877
|
}
|
1863
|
-
const openai = new OpenAI({ apiKey:
|
1878
|
+
const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
|
1864
1879
|
|
1865
1880
|
const sliceMachineConfig = await this.project.getSliceMachineConfig();
|
1866
1881
|
const libraryIDs = sliceMachineConfig.libraries || [];
|
1867
1882
|
const DEFAULT_LIBRARY_ID = libraryIDs[0];
|
1868
1883
|
|
1869
|
-
|
1870
|
-
"
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1884
|
+
let retry: {
|
1885
|
+
[key in "MODEL" | "MOCKS" | "CODE" | "APPEARANCE"]: number;
|
1886
|
+
}[] = args.sliceImages.map((_) => ({
|
1887
|
+
MODEL: 0,
|
1888
|
+
MOCKS: 0,
|
1889
|
+
CODE: 0,
|
1890
|
+
APPEARANCE: 0,
|
1891
|
+
}));
|
1892
|
+
|
1893
|
+
async function callAI<ReturnType extends Record<string, unknown>>({
|
1894
|
+
ai,
|
1895
|
+
sliceIndex,
|
1896
|
+
stepName,
|
1897
|
+
systemPrompt,
|
1898
|
+
imageFile,
|
1899
|
+
textContent,
|
1900
|
+
}: {
|
1901
|
+
ai: "OPENAI" | "AWS";
|
1902
|
+
sliceIndex: number;
|
1903
|
+
stepName: "MODEL" | "MOCKS" | "CODE" | "APPEARANCE";
|
1904
|
+
systemPrompt: string;
|
1905
|
+
imageFile?: Uint8Array;
|
1906
|
+
textContent?: string;
|
1907
|
+
}): Promise<ReturnType> {
|
1908
|
+
let resultText: string | undefined;
|
1909
|
+
|
1910
|
+
if (ai === "OPENAI") {
|
1911
|
+
const messages: Array<ChatCompletionMessageParam> = [
|
1912
|
+
{ role: "system", content: systemPrompt },
|
1913
|
+
];
|
1914
|
+
|
1915
|
+
const userContent: Array<
|
1916
|
+
| {
|
1917
|
+
type: "text";
|
1918
|
+
text: string;
|
1919
|
+
}
|
1920
|
+
| {
|
1921
|
+
type: "image_url";
|
1922
|
+
image_url: { url: string };
|
1923
|
+
}
|
1924
|
+
> = [];
|
1925
|
+
|
1926
|
+
if (imageFile) {
|
1927
|
+
userContent.push({
|
1928
|
+
type: "image_url",
|
1929
|
+
image_url: {
|
1930
|
+
url:
|
1931
|
+
"data:image/png;base64," +
|
1932
|
+
Buffer.from(imageFile).toString("base64"),
|
1933
|
+
},
|
1934
|
+
});
|
1935
|
+
}
|
1874
1936
|
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
console.log(process.cwd());
|
1937
|
+
if (textContent) {
|
1938
|
+
userContent.push({ type: "text", text: textContent });
|
1939
|
+
}
|
1879
1940
|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
return new Uint8Array(buffer);
|
1887
|
-
}),
|
1888
|
-
);
|
1941
|
+
if (userContent.length > 0) {
|
1942
|
+
messages.push({
|
1943
|
+
role: "user",
|
1944
|
+
content: userContent,
|
1945
|
+
});
|
1946
|
+
}
|
1889
1947
|
|
1890
|
-
|
1891
|
-
|
1948
|
+
const response = await openai.chat.completions.create({
|
1949
|
+
model: "gpt-4o",
|
1950
|
+
messages,
|
1951
|
+
response_format: { type: "json_object" },
|
1952
|
+
});
|
1953
|
+
|
1954
|
+
console.log(
|
1955
|
+
`Generated response for ${stepName} - ${sliceIndex}:`,
|
1956
|
+
JSON.stringify(response),
|
1957
|
+
);
|
1958
|
+
|
1959
|
+
resultText = response.choices[0]?.message?.content?.trim();
|
1960
|
+
} else if (ai === "AWS") {
|
1961
|
+
const messages: Array<Message> = [];
|
1962
|
+
|
1963
|
+
if (imageFile) {
|
1964
|
+
messages.push({
|
1965
|
+
role: "user",
|
1966
|
+
content: [
|
1967
|
+
{
|
1968
|
+
image: { format: "png", source: { bytes: imageFile } },
|
1969
|
+
},
|
1970
|
+
],
|
1971
|
+
});
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
if (textContent) {
|
1975
|
+
messages.push({
|
1976
|
+
role: "user",
|
1977
|
+
content: [
|
1978
|
+
{
|
1979
|
+
text: textContent,
|
1980
|
+
},
|
1981
|
+
],
|
1982
|
+
});
|
1983
|
+
}
|
1892
1984
|
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1985
|
+
const command = new ConverseCommand({
|
1986
|
+
modelId: "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
|
1987
|
+
system: [{ text: systemPrompt }],
|
1988
|
+
messages: messages,
|
1989
|
+
});
|
1990
|
+
|
1991
|
+
const response = await bedrockClient.send(command);
|
1992
|
+
|
1993
|
+
console.log(
|
1994
|
+
`Generated response for ${stepName} - ${sliceIndex}:`,
|
1995
|
+
JSON.stringify(response),
|
1996
|
+
);
|
1997
|
+
|
1998
|
+
resultText = response.output?.message?.content?.[0]?.text?.trim();
|
1999
|
+
}
|
2000
|
+
|
2001
|
+
async function retryCall(error: string): Promise<ReturnType> {
|
2002
|
+
if (retry[sliceIndex][stepName] < 3) {
|
2003
|
+
retry[sliceIndex][stepName]++;
|
2004
|
+
console.log(
|
2005
|
+
`Retrying ${retry[sliceIndex][stepName]} ${stepName} for slice ${sliceIndex}.`,
|
2006
|
+
error,
|
1900
2007
|
);
|
1901
|
-
return buffer.toString();
|
1902
|
-
}),
|
1903
|
-
);
|
1904
2008
|
|
1905
|
-
|
1906
|
-
|
2009
|
+
return await callAI({
|
2010
|
+
ai,
|
2011
|
+
sliceIndex,
|
2012
|
+
stepName,
|
2013
|
+
systemPrompt,
|
2014
|
+
imageFile,
|
2015
|
+
textContent,
|
2016
|
+
});
|
2017
|
+
}
|
2018
|
+
|
2019
|
+
throw new Error(error);
|
2020
|
+
}
|
1907
2021
|
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
2022
|
+
if (!resultText) {
|
2023
|
+
return await retryCall(
|
2024
|
+
`No valid response was generated for ${stepName}.`,
|
2025
|
+
);
|
2026
|
+
}
|
1912
2027
|
|
1913
|
-
|
2028
|
+
try {
|
2029
|
+
return JSON.parse(resultText);
|
2030
|
+
} catch (error) {
|
2031
|
+
return await retryCall(
|
2032
|
+
`Failed to parse AI response for ${stepName}: ` + error,
|
2033
|
+
);
|
2034
|
+
}
|
1914
2035
|
}
|
1915
2036
|
|
1916
2037
|
/**
|
@@ -2241,436 +2362,328 @@ type GroupField = {
|
|
2241
2362
|
* Calls the AI to generate the slice model.
|
2242
2363
|
*/
|
2243
2364
|
async function generateSliceModel(
|
2365
|
+
sliceIndex: number,
|
2244
2366
|
imageFile: Uint8Array,
|
2245
|
-
codeFile: string,
|
2246
2367
|
): Promise<SharedSlice> {
|
2247
2368
|
const systemPrompt = `
|
2248
|
-
You are an expert in Prismic content modeling
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2252
|
-
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2269
|
-
|
2270
|
-
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2369
|
+
You are an **expert in Prismic content modeling**. Using the **image and code provided**, generate a **valid Prismic JSON model** for the slice described below.
|
2370
|
+
|
2371
|
+
**STRICT MODELING RULES (NO EXCEPTIONS):**
|
2372
|
+
- **Use the TypeScript schema provided as your reference**.
|
2373
|
+
- **Absolutely all fields must be placed under the "primary" object**.
|
2374
|
+
- **Do not create groups or collections for single-image content** (background images must be a single image field).
|
2375
|
+
- **Ensure each field has appropriate placeholders, labels, and configurations**.
|
2376
|
+
- **Never generate a Link/Button text field—only the Link/Button field itself** with \`"allowText": true\`.
|
2377
|
+
- **Include all fields visible in the provided image**, do not forget any field and everything should be covered.
|
2378
|
+
- **Repeated fields must always be grouped**:
|
2379
|
+
- **Identify when field are part of a group**, when there is a repetition of a field or multiple fields together use a Group field.
|
2380
|
+
- **DO NOT** create individually numbered fields like \`feature1\`, \`feature2\`, \`feature3\`. Instead, define a single **Group field** (e.g., \`features\`) and move all repeated items inside it.
|
2381
|
+
- **Differentiate Prismic fields from decorative elements:**
|
2382
|
+
- If an element in the image is purely visual/decorative, **do not include it in the model**.
|
2383
|
+
- Use the **code as the source of truth** to determine what should be a field.
|
2384
|
+
- If an element is an image in the code, it **must also be an image field in Prismic** (strict 1:1 mapping).
|
2385
|
+
- **Handle repeated content correctly:**
|
2386
|
+
- **If an image, text, or link is repeated, always use a Group field**—do not create individual fields.
|
2387
|
+
- **If multiple fields are repeated together, they must be inside a single Group field**.
|
2388
|
+
- **Strictly forbid nesting of groups:**
|
2389
|
+
- **NEVER put a Group inside another Group field**.
|
2390
|
+
- **Group fields CANNOT be nested for any reason**—this is **strictly prohibited** even for navigation structures like headers or footers.
|
2391
|
+
- **Do not use the "items" field**:
|
2392
|
+
- **All repeatable fields must be defined as Group fields under "primary"**.
|
2393
|
+
- **"items" must never appear in the final JSON output**.
|
2394
|
+
- **Do not create more than one SliceVariation**, only one variation is enough to create the model.
|
2395
|
+
|
2396
|
+
**STRICT FIELD NAMING & CONTENT RULES:**
|
2397
|
+
- **Replace placeholders in the existing slice template** (\`<ID_TO_CHANGE>\`, \`<NAME_TO_CHANGE>\`, etc.).
|
2398
|
+
- **Field placeholders must be very short**—do **not** put actual image content inside placeholders.
|
2399
|
+
- **Field labels and IDs must define the field's purpose, not its content**.
|
2400
|
+
- **Slice name, ID, and description must describe the slice's function, not its content**.
|
2401
|
+
- The slice name and ID must be **generic and reusable**, defining what the slice **does**, not what it is used for.
|
2402
|
+
- **DO NOT name the slice after a specific topic, content type, or industry. Instead, name it based on its structure and function.
|
2403
|
+
|
2404
|
+
**STRICT JSON OUTPUT FORMAT (NO MARKDOWN OR EXTRA TEXT):**
|
2405
|
+
- **Return ONLY a valid JSON object**—no extra text, comments, or formatting.
|
2406
|
+
- **The response must be directly parseable** using \`JSON.parse(output)\`.
|
2407
|
+
- **Do not wrap the output in markdown (\`\`\`\`json\`) or any other formatting.**
|
2408
|
+
|
2409
|
+
**VALIDATION REQUIREMENT:**
|
2410
|
+
- Before returning, **validate that \`JSON.parse(output)\` runs without errors**.
|
2411
|
+
- If there is **any extra text, markdown, or incorrect structure**, **rewrite the response before returning**.
|
2412
|
+
|
2413
|
+
**REFERENCE SCHEMA (Follow this exactly):**
|
2275
2414
|
${SHARED_SLICE_SCHEMA}
|
2276
2415
|
|
2277
|
-
|
2416
|
+
**EXISTING SLICE TO UPDATE (Modify strictly according to the rules above):**
|
2278
2417
|
${JSON.stringify(DEFAULT_SLICE_MODEL)}
|
2279
2418
|
`.trim();
|
2280
2419
|
|
2281
|
-
const
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
|
2287
|
-
type: "image_url",
|
2288
|
-
image_url: {
|
2289
|
-
url:
|
2290
|
-
"data:image/png;base64," +
|
2291
|
-
Buffer.from(imageFile).toString("base64"),
|
2292
|
-
},
|
2293
|
-
},
|
2294
|
-
{ type: "text", text: codeFile },
|
2295
|
-
],
|
2296
|
-
},
|
2297
|
-
];
|
2298
|
-
const response: OpenAI.Chat.Completions.ChatCompletion & {
|
2299
|
-
_request_id?: string | null;
|
2300
|
-
} = await openai.chat.completions.create({
|
2301
|
-
model: "gpt-4o",
|
2302
|
-
messages,
|
2303
|
-
response_format: {
|
2304
|
-
type: "json_object",
|
2305
|
-
},
|
2420
|
+
const generatedModel = await callAI<SharedSlice>({
|
2421
|
+
ai: "OPENAI",
|
2422
|
+
sliceIndex,
|
2423
|
+
stepName: "MODEL",
|
2424
|
+
systemPrompt,
|
2425
|
+
imageFile,
|
2306
2426
|
});
|
2307
2427
|
|
2308
|
-
|
2309
|
-
|
2310
|
-
const resultText = response.choices[0]?.message?.content?.trim();
|
2311
|
-
if (!resultText) {
|
2312
|
-
throw new Error("No valid slice model was generated.");
|
2313
|
-
}
|
2314
|
-
|
2315
|
-
try {
|
2316
|
-
const generatedModel = JSON.parse(resultText);
|
2317
|
-
|
2318
|
-
return generatedModel;
|
2319
|
-
} catch (error) {
|
2320
|
-
throw new Error("Failed to parse AI response for model: " + error);
|
2321
|
-
}
|
2428
|
+
return generatedModel;
|
2322
2429
|
}
|
2323
2430
|
|
2324
2431
|
/**
|
2325
2432
|
* Calls the AI endpoint to generate mocks.
|
2326
2433
|
*/
|
2327
2434
|
async function generateSliceMocks(
|
2435
|
+
sliceIndex: number,
|
2328
2436
|
imageFile: Uint8Array,
|
2329
2437
|
existingMocks: SharedSliceContent[],
|
2330
2438
|
): Promise<SharedSliceContent[]> {
|
2331
|
-
// Build a prompt focused solely on updating the mocks.
|
2332
2439
|
const systemPrompt = `
|
2333
|
-
You are a seasoned frontend engineer with deep expertise in Prismic slices
|
2334
|
-
Your task is to update the provided mocks template based
|
2335
|
-
|
2336
|
-
|
2337
|
-
-
|
2338
|
-
- Do not
|
2339
|
-
-
|
2340
|
-
-
|
2341
|
-
-
|
2342
|
-
-
|
2343
|
-
|
2344
|
-
|
2345
|
-
|
2346
|
-
-
|
2347
|
-
-
|
2348
|
-
- Never
|
2349
|
-
|
2350
|
-
|
2440
|
+
You are a **seasoned frontend engineer** with **deep expertise in Prismic slices**.
|
2441
|
+
Your task is to **update the provided mocks template** based **only** on the visible text content in the provided image.
|
2442
|
+
|
2443
|
+
**STRICT UPDATE GUIDELINES:**
|
2444
|
+
- **Do no create content, only take visible text from the image.**
|
2445
|
+
- **Do not modify the overall structure of the mocks template.**
|
2446
|
+
- **Strictly update text content only.**
|
2447
|
+
- **Do not touch images or image-related fields.**
|
2448
|
+
- **If a repeated item appears in a group, match the exact number of group items seen in the image.**
|
2449
|
+
- **Do not modify metadata, field properties, or structure.** This includes:
|
2450
|
+
- Do **not** change the \`"key"\` property of links.
|
2451
|
+
- Do **not** modify \`StructuredText\` properties such as \`"direction"\`, \`"spans"\`, or \`"type"\`.
|
2452
|
+
- Do **not** alter field nesting or object structure.
|
2453
|
+
- **For StructuredText fields, maintain all existing structure and properties**—**only replace text content**.
|
2454
|
+
- **Ensure that only visible text in the image is updated**—do not generate or assume content.
|
2455
|
+
- **Never modify image fields**—image references and properties must remain unchanged.
|
2456
|
+
|
2457
|
+
**STRICT JSON OUTPUT FORMAT:**
|
2458
|
+
- **Return ONLY a valid JSON object**—no extra text, explanations, or formatting.
|
2459
|
+
- **The response must be directly parseable** using \`JSON.parse(output)\`.
|
2460
|
+
- **Do not wrap the output in markdown (\`\`\`\`json\`) or any other formatting.**
|
2461
|
+
|
2462
|
+
**VALIDATION REQUIREMENT:**
|
2463
|
+
- Before returning, **validate that \`JSON.parse(output)\` runs without errors**.
|
2464
|
+
- If there is **any extra text, markdown, or incorrect structure**, **rewrite the response before returning**.
|
2465
|
+
|
2466
|
+
**EXISTING MOCKS TEMPLATE (To be updated with the visible text from the image only):**
|
2351
2467
|
${JSON.stringify(existingMocks)}
|
2352
2468
|
`.trim();
|
2353
2469
|
|
2354
|
-
const
|
2355
|
-
|
2356
|
-
|
2357
|
-
|
2358
|
-
|
2359
|
-
|
2360
|
-
type: "image_url",
|
2361
|
-
image_url: {
|
2362
|
-
url:
|
2363
|
-
"data:image/png;base64," +
|
2364
|
-
Buffer.from(imageFile).toString("base64"),
|
2365
|
-
},
|
2366
|
-
},
|
2367
|
-
],
|
2368
|
-
},
|
2369
|
-
];
|
2370
|
-
const response: OpenAI.Chat.Completions.ChatCompletion & {
|
2371
|
-
_request_id?: string | null;
|
2372
|
-
} = await openai.chat.completions.create({
|
2373
|
-
model: "gpt-4o",
|
2374
|
-
messages,
|
2375
|
-
response_format: {
|
2376
|
-
type: "json_object",
|
2377
|
-
},
|
2470
|
+
const updatedMock = await callAI<SharedSliceContent>({
|
2471
|
+
ai: "OPENAI",
|
2472
|
+
sliceIndex,
|
2473
|
+
stepName: "MOCKS",
|
2474
|
+
systemPrompt,
|
2475
|
+
imageFile,
|
2378
2476
|
});
|
2379
2477
|
|
2380
|
-
|
2478
|
+
return [updatedMock];
|
2479
|
+
}
|
2381
2480
|
|
2382
|
-
|
2383
|
-
|
2384
|
-
|
2385
|
-
|
2481
|
+
const SLICE_CODE_EXAMPLE = `
|
2482
|
+
-----------------------------------------------------------
|
2483
|
+
import { FC } from "react";
|
2484
|
+
import { Content } from "@prismicio/client";
|
2485
|
+
import { SliceComponentProps, PrismicRichText } from "@prismicio/react";
|
2486
|
+
import { PrismicNextImage, PrismicNextLink } from "@prismicio/next";
|
2487
|
+
|
2488
|
+
export type PascalNameToReplaceProps =
|
2489
|
+
SliceComponentProps<Content.PascalNameToReplaceSlice>;
|
2490
|
+
|
2491
|
+
const PascalNameToReplace: FC<PascalNameToReplaceProps> = ({ slice }) => {
|
2492
|
+
return (
|
2493
|
+
<section
|
2494
|
+
data-slice-type={slice.slice_type}
|
2495
|
+
data-slice-variation={slice.variation}
|
2496
|
+
className="es-bounded es-alternate-grid"
|
2497
|
+
>
|
2498
|
+
<PrismicNextLink
|
2499
|
+
className="es-alternate-grid__button"
|
2500
|
+
field={slice.primary.buttonLink}
|
2501
|
+
/>
|
2502
|
+
<div className="es-alternate-grid__content">
|
2503
|
+
<PrismicNextImage
|
2504
|
+
field={slice.primary.image}
|
2505
|
+
className="es-alternate-grid__image"
|
2506
|
+
/>
|
2507
|
+
<div className="es-alternate-grid__primary-content">
|
2508
|
+
<div className="es-alternate-grid__primary-content__intro">
|
2509
|
+
<p className="es-alternate-grid__primary-content__intro__eyebrow">
|
2510
|
+
{slice.primary.eyebrowHeadline}
|
2511
|
+
</p>
|
2512
|
+
<div className="es-alternate-grid__primary-content__intro__headline">
|
2513
|
+
<PrismicRichText field={slice.primary.title} />
|
2514
|
+
</div>
|
2515
|
+
<div className="es-alternate-grid__primary-content__intro__description">
|
2516
|
+
<PrismicRichText field={slice.primary.description} />
|
2517
|
+
</div>
|
2518
|
+
</div>
|
2386
2519
|
|
2387
|
-
|
2388
|
-
|
2520
|
+
<div className="es-alternate-grid__primary-content__stats">
|
2521
|
+
{slice.primary.stats.map((stat, i) => (
|
2522
|
+
<div key={\`stat-$\{i + 1\}\`} className="es-alternate-grid__stat">
|
2523
|
+
<div className="es-alternate-grid__stat__heading">
|
2524
|
+
<PrismicRichText field={stat.title} />
|
2525
|
+
</div>
|
2526
|
+
<div className="es-alternate-grid__stat__description">
|
2527
|
+
<PrismicRichText field={stat.description} />
|
2528
|
+
</div>
|
2529
|
+
</div>
|
2530
|
+
))}
|
2531
|
+
</div>
|
2532
|
+
</div>
|
2533
|
+
</div>
|
2534
|
+
</section>
|
2535
|
+
);
|
2536
|
+
};
|
2389
2537
|
|
2390
|
-
|
2391
|
-
|
2392
|
-
|
2393
|
-
}
|
2394
|
-
}
|
2538
|
+
export default PascalNameToReplace;
|
2539
|
+
-----------------------------------------------------------
|
2540
|
+
`.trim();
|
2395
2541
|
|
2396
2542
|
/**
|
2397
2543
|
* Calls the AI endpoint to generate the slice React component.
|
2398
2544
|
*/
|
2399
2545
|
async function generateSliceComponentCode(
|
2546
|
+
sliceIndex: number,
|
2400
2547
|
imageFile: Uint8Array,
|
2401
|
-
codeFile: string,
|
2402
2548
|
updatedSlice: SharedSlice,
|
2403
2549
|
): Promise<string> {
|
2404
2550
|
const systemPrompt = `
|
2405
|
-
You are a seasoned frontend engineer with deep expertise in Prismic slices
|
2406
|
-
Your task is to generate a fully isolated React component
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
- Be self-contained.
|
2411
|
-
-
|
2412
|
-
-
|
2413
|
-
-
|
2414
|
-
- Ensure
|
2415
|
-
|
2416
|
-
|
2417
|
-
|
2418
|
-
|
2419
|
-
|
2420
|
-
-
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
-
|
2426
|
-
-
|
2427
|
-
|
2428
|
-
|
2429
|
-
-
|
2430
|
-
-
|
2431
|
-
- The
|
2432
|
-
|
2433
|
-
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
|
2439
|
-
|
2440
|
-
|
2441
|
-
|
2442
|
-
|
2443
|
-
const PascalNameToReplace: FC<PascalNameToReplaceProps> = ({ slice }) => {
|
2444
|
-
return (
|
2445
|
-
<section
|
2446
|
-
data-slice-type={slice.slice_type}
|
2447
|
-
data-slice-variation={slice.variation}
|
2448
|
-
className="es-bounded es-alternate-grid"
|
2449
|
-
>
|
2450
|
-
<PrismicNextLink
|
2451
|
-
className="es-alternate-grid__button"
|
2452
|
-
field={slice.primary.buttonLink}
|
2453
|
-
/>
|
2454
|
-
<div className="es-alternate-grid__content">
|
2455
|
-
<PrismicNextImage
|
2456
|
-
field={slice.primary.image}
|
2457
|
-
className="es-alternate-grid__image"
|
2458
|
-
/>
|
2459
|
-
<div className="es-alternate-grid__primary-content">
|
2460
|
-
<div className="es-alternate-grid__primary-content__intro">
|
2461
|
-
<p className="es-alternate-grid__primary-content__intro__eyebrow">
|
2462
|
-
{slice.primary.eyebrowHeadline}
|
2463
|
-
</p>
|
2464
|
-
<div className="es-alternate-grid__primary-content__intro__headline">
|
2465
|
-
<PrismicRichText field={slice.primary.title} />
|
2466
|
-
</div>
|
2467
|
-
<div className="es-alternate-grid__primary-content__intro__description">
|
2468
|
-
<PrismicRichText field={slice.primary.description} />
|
2469
|
-
</div>
|
2470
|
-
</div>
|
2471
|
-
|
2472
|
-
<div className="es-alternate-grid__primary-content__stats">
|
2473
|
-
{slice.primary.stats.map((stat, i) => (
|
2474
|
-
<div key={\`stat-$\{i + 1\}\`} className="es-alternate-grid__stat">
|
2475
|
-
<div className="es-alternate-grid__stat__heading">
|
2476
|
-
<PrismicRichText field={stat.title} />
|
2477
|
-
</div>
|
2478
|
-
<div className="es-alternate-grid__stat__description">
|
2479
|
-
<PrismicRichText field={stat.description} />
|
2480
|
-
</div>
|
2481
|
-
</div>
|
2482
|
-
))}
|
2483
|
-
</div>
|
2484
|
-
</div>
|
2485
|
-
</div>
|
2486
|
-
</section>
|
2487
|
-
);
|
2488
|
-
};
|
2489
|
-
|
2490
|
-
export default PascalNameToReplace;
|
2491
|
-
-----------------------------------------------------------
|
2492
|
-
|
2493
|
-
Model of the slice:
|
2551
|
+
You are a **seasoned frontend engineer** with **deep expertise in Prismic slices**.
|
2552
|
+
Your task is to generate a **fully isolated React component** for a Prismic slice, **focusing ONLY on structure (HTML) without styling**.
|
2553
|
+
|
2554
|
+
**STRICT STRUCTURAL GUIDELINES:**
|
2555
|
+
- **Do not include styling.** Focus **100% on correct structure**.
|
2556
|
+
- **Be self-contained.** The component must work in isolation.
|
2557
|
+
- **Follow the structure provided in the example.** Do not introduce **any variations**.
|
2558
|
+
- **Use all fields provided in the model**—do not omit or invent fields.
|
2559
|
+
- **Never access a field using** \`<field>.value\`. Always follow the example pattern with just \`<field>\`.
|
2560
|
+
- **Ensure correct mapping of field types:**
|
2561
|
+
- **StructuredText** → \`PrismicRichText\`
|
2562
|
+
- **Image field** → \`PrismicNextImage\`
|
2563
|
+
- **Text field** → Standard \`<p>\` element
|
2564
|
+
- **Link field** → \`PrismicNextLink\`
|
2565
|
+
- **Group field** → Map to the correct structure based on the example.
|
2566
|
+
- **Maintain W3C-compliant HTML.** Do not place \`PrismicRichText\` inside \`<h1>\`, \`<p>\`, or other invalid elements.
|
2567
|
+
|
2568
|
+
**PRISMIC COMPONENT USAGE RULES:**
|
2569
|
+
- **Links must use \`PrismicNextLink\`**, passing only the \`field\` (no manual text extraction).
|
2570
|
+
- **\`PrismicNextLink\` must never be opened manually**—pass the field directly as in the example.
|
2571
|
+
- **\`PrismicRichText\` cannot have a \`style\` prop**.
|
2572
|
+
- **Imports must be identical to the provided example**.
|
2573
|
+
|
2574
|
+
**STRICT JSON OUTPUT FORMAT**
|
2575
|
+
- **Return ONLY a valid JSON object** with **one key**: \`"componentCode"\`.
|
2576
|
+
- **No markdown (\`\`\`\`json\`), no comments, no text before or after—ONLY pure JSON**.
|
2577
|
+
- **The response MUST start with \`{\` and end with \`}\` exactly, do not start with a sentence explaining what you will do.**
|
2578
|
+
- **Ensure the output is directly parseable** with \`JSON.parse(output)\`.
|
2579
|
+
- **All strings must use double quotes (\`"\`).** Do not use single quotes or template literals.
|
2580
|
+
- **Escape all embedded double quotes (\`\"\`) and backslashes (\`\\\`).**
|
2581
|
+
- **The output must not contain raw control characters** (newline, tab, etc.); use escape sequences instead.
|
2582
|
+
|
2583
|
+
**Before returning, VALIDATE that \`JSON.parse(output)\` runs without errors.**
|
2584
|
+
|
2585
|
+
**EXAMPLE OF A FULLY ISOLATED SLICE COMPONENT (Follow this strictly):**
|
2586
|
+
${SLICE_CODE_EXAMPLE}
|
2587
|
+
|
2588
|
+
**SLICE MODEL (Use this as the exact reference):**
|
2494
2589
|
${JSON.stringify(updatedSlice)}
|
2495
2590
|
`.trim();
|
2496
2591
|
|
2497
|
-
const
|
2498
|
-
|
2499
|
-
|
2500
|
-
|
2501
|
-
|
2502
|
-
|
2503
|
-
type: "image_url",
|
2504
|
-
image_url: {
|
2505
|
-
url:
|
2506
|
-
"data:image/png;base64," +
|
2507
|
-
Buffer.from(imageFile).toString("base64"),
|
2508
|
-
},
|
2509
|
-
},
|
2510
|
-
{ type: "text", text: codeFile },
|
2511
|
-
],
|
2512
|
-
},
|
2513
|
-
];
|
2514
|
-
const response: OpenAI.Chat.Completions.ChatCompletion & {
|
2515
|
-
_request_id?: string | null;
|
2516
|
-
} = await openai.chat.completions.create({
|
2517
|
-
model: "gpt-4o",
|
2518
|
-
messages,
|
2519
|
-
response_format: {
|
2520
|
-
type: "json_object",
|
2521
|
-
},
|
2592
|
+
const parsed = await callAI<{ componentCode: string }>({
|
2593
|
+
ai: "AWS",
|
2594
|
+
sliceIndex,
|
2595
|
+
stepName: "CODE",
|
2596
|
+
systemPrompt,
|
2597
|
+
imageFile,
|
2522
2598
|
});
|
2523
2599
|
|
2524
|
-
|
2525
|
-
"
|
2526
|
-
JSON.stringify(response),
|
2527
|
-
);
|
2528
|
-
|
2529
|
-
const resultText = response.choices[0]?.message?.content?.trim();
|
2530
|
-
if (!resultText) {
|
2531
|
-
throw new Error("No valid slice component code was generated.");
|
2600
|
+
if (!parsed.componentCode) {
|
2601
|
+
throw new Error("Missing key 'componentCode' in AI response.");
|
2532
2602
|
}
|
2533
2603
|
|
2534
|
-
|
2535
|
-
const parsed = JSON.parse(resultText);
|
2536
|
-
if (!parsed.componentCode) {
|
2537
|
-
throw new Error("Missing key 'componentCode' in AI response.");
|
2538
|
-
}
|
2539
|
-
return parsed.componentCode;
|
2540
|
-
} catch (error) {
|
2541
|
-
throw new Error(
|
2542
|
-
"Failed to parse AI response for component code: " + error,
|
2543
|
-
);
|
2544
|
-
}
|
2604
|
+
return parsed.componentCode;
|
2545
2605
|
}
|
2546
2606
|
|
2547
2607
|
async function generateSliceComponentCodeAppearance(
|
2608
|
+
sliceIndex: number,
|
2548
2609
|
imageFile: Uint8Array,
|
2549
|
-
codeFile: string,
|
2550
|
-
globalStyle: string,
|
2551
2610
|
componentCode: string,
|
2552
2611
|
): Promise<string> {
|
2553
2612
|
const systemPrompt = `
|
2554
|
-
You are a seasoned frontend engineer with deep expertise in Prismic slices
|
2555
|
-
Your task is to apply
|
2556
|
-
The branding is
|
2557
|
-
|
2558
|
-
|
2559
|
-
-
|
2560
|
-
-
|
2561
|
-
-
|
2562
|
-
|
2563
|
-
|
2564
|
-
-
|
2565
|
-
-
|
2566
|
-
-
|
2567
|
-
-
|
2568
|
-
-
|
2569
|
-
-
|
2570
|
-
-
|
2571
|
-
-
|
2572
|
-
-
|
2573
|
-
|
2574
|
-
|
2575
|
-
|
2576
|
-
|
2577
|
-
|
2578
|
-
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
2584
|
-
|
2613
|
+
You are a **seasoned frontend engineer** with **deep expertise in Prismic slices**.
|
2614
|
+
Your task is to **apply branding (appearance) strictly based on the provided image and code input**.
|
2615
|
+
The **branding is CRITICAL**—the slice you create **must perfectly match the visual appearance** of the provided slice image.
|
2616
|
+
|
2617
|
+
**STRICT GUIDELINES TO FOLLOW (NO EXCEPTIONS):**
|
2618
|
+
- **DO NOT** modify the structure of the code—**ONLY apply styling**. Your role is **purely styling-related**.
|
2619
|
+
- **NO external dependencies**—use **only inline** styling.
|
2620
|
+
- **VISUAL ACCURACY IS MANDATORY**—your goal is to make the output **visually identical** to the provided image.
|
2621
|
+
|
2622
|
+
**MUST strictly respect the following:**
|
2623
|
+
- **Background color** → Must **exactly match** the image. If unsure, **do not apply** any background color.
|
2624
|
+
- **Padding & margin** → Must be **pixel-perfect** per the provided image.
|
2625
|
+
- **Font size, color, and type** → Must match exactly. If the exact font is unavailable, choose the **closest possible match**.
|
2626
|
+
- **Typography precision** → If the font-family does not match, **the output is incorrect**.
|
2627
|
+
- **Color accuracy** → Use **ONLY the colors visible** in the provided image.
|
2628
|
+
- **Element positioning** → Elements **must be placed exactly** as seen in the provided image.
|
2629
|
+
- **Element sizes** → Every element **must match** the provided image in width, height, and proportions.
|
2630
|
+
- **Overall proportions** → The slice must maintain **identical proportions** to the provided image.
|
2631
|
+
- **Image constraints** → Images **must** maintain their **original aspect ratio**. Use explicit \`width\` and \`height\` constraints with an explicit pixels value. Avoid \`width: auto\`, \`height: auto\`, \`width: 100%\` or \`height: 100%\`.
|
2632
|
+
- **Repetitions & layout** → Ensure **consistent styling** across repeated items. The **layout direction (horizontal/vertical)** must match the image.
|
2633
|
+
- **Animations** → Handle animations as seen in the image, but **keep them fast and subtle** (avoid long animations).
|
2634
|
+
|
2635
|
+
**IMPORTANT RULES:**
|
2636
|
+
1. **DO NOT modify any non-styling code**.
|
2637
|
+
- **Everything from the first import to the last export must remain unchanged**.
|
2638
|
+
- **Only add styling** on top of the existing structure.
|
2639
|
+
|
2640
|
+
2. **STRICT JSON OUTPUT FORMAT**
|
2641
|
+
- Return a **valid JSON object** with **one key only**: \`"componentCode"\`.
|
2642
|
+
- **NO markdown, NO code blocks, NO text before or after**—only **pure JSON**.
|
2643
|
+
- The response **must start and end directly with \`{ "componentCode": ... }\`**.
|
2644
|
+
- Ensure the output is **directly parseable** using \`JSON.parse(output)\`.
|
2645
|
+
|
2646
|
+
3. **INLINE \`<style>\` RULES**
|
2647
|
+
- Use **only inline** \`<style>\` tags (not \`<style jsx>\`).
|
2648
|
+
- Ensure **all CSS is valid** and matches the image precisely.
|
2649
|
+
- **Use backtick inside the \`<style>\` tag like this: <style>{\`...\`}</style>**.
|
2650
|
+
- **Do NOT escape the backtick (\`\`\`) inside the \`<style>\` tag**.
|
2651
|
+
|
2652
|
+
**Before returning, VALIDATE that \`JSON.parse(output)\` runs without errors.**
|
2653
|
+
|
2654
|
+
**EXISTING CODE (to apply branding on):**
|
2585
2655
|
${componentCode}
|
2586
|
-
|
2587
|
-
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
|
2593
|
-
|
2594
|
-
{
|
2595
|
-
role: "user",
|
2596
|
-
content: [
|
2597
|
-
{
|
2598
|
-
type: "image_url",
|
2599
|
-
image_url: {
|
2600
|
-
url:
|
2601
|
-
"data:image/png;base64," +
|
2602
|
-
Buffer.from(imageFile).toString("base64"),
|
2603
|
-
},
|
2604
|
-
},
|
2605
|
-
{ type: "text", text: codeFile },
|
2606
|
-
],
|
2607
|
-
},
|
2608
|
-
];
|
2609
|
-
const response: OpenAI.Chat.Completions.ChatCompletion & {
|
2610
|
-
_request_id?: string | null;
|
2611
|
-
} = await openai.chat.completions.create({
|
2612
|
-
model: "gpt-4o",
|
2613
|
-
messages,
|
2614
|
-
response_format: {
|
2615
|
-
type: "json_object",
|
2616
|
-
},
|
2656
|
+
`.trim();
|
2657
|
+
|
2658
|
+
const parsed = await callAI<{ componentCode: string }>({
|
2659
|
+
ai: "AWS",
|
2660
|
+
sliceIndex,
|
2661
|
+
stepName: "APPEARANCE",
|
2662
|
+
systemPrompt,
|
2663
|
+
imageFile,
|
2617
2664
|
});
|
2618
2665
|
|
2619
|
-
|
2620
|
-
"
|
2621
|
-
JSON.stringify(response),
|
2622
|
-
);
|
2623
|
-
|
2624
|
-
const resultText = response.choices[0]?.message?.content?.trim();
|
2625
|
-
if (!resultText) {
|
2626
|
-
throw new Error("No valid slice component code was generated.");
|
2666
|
+
if (!parsed.componentCode) {
|
2667
|
+
throw new Error("Missing key 'componentCode' in AI response.");
|
2627
2668
|
}
|
2628
2669
|
|
2629
|
-
|
2630
|
-
const parsed = JSON.parse(resultText);
|
2631
|
-
if (!parsed.componentCode) {
|
2632
|
-
throw new Error("Missing key 'componentCode' in AI response.");
|
2633
|
-
}
|
2634
|
-
return parsed.componentCode;
|
2635
|
-
} catch (error) {
|
2636
|
-
throw new Error(
|
2637
|
-
"Failed to parse AI response for component code appearance: " + error,
|
2638
|
-
);
|
2639
|
-
}
|
2670
|
+
return parsed.componentCode;
|
2640
2671
|
}
|
2641
2672
|
|
2642
2673
|
try {
|
2643
|
-
let slices: {
|
2644
|
-
sliceImage: Uint8Array;
|
2645
|
-
codeFile: string;
|
2646
|
-
}[] = [];
|
2647
|
-
|
2648
|
-
const folderPath = KNOWNED_WEBSITE_URLS[args.websiteUrl];
|
2649
|
-
|
2650
|
-
console.log("STEP 1: Get the slices images from the folder.");
|
2651
|
-
const sliceImages = await readImagesFromFolder(`${folderPath}/images`);
|
2652
|
-
|
2653
|
-
console.log("STEP 2: Get the slices codes from the folder.");
|
2654
|
-
const sliceCodes = await readCodeFromFolder(`${folderPath}/code`);
|
2655
|
-
|
2656
|
-
slices = sliceImages.map((sliceImage, index) => ({
|
2657
|
-
sliceImage,
|
2658
|
-
codeFile: sliceCodes[index],
|
2659
|
-
}));
|
2660
|
-
|
2661
2674
|
// Loop in parallel over each slice image and html code and generate the slice model, mocks and code.
|
2662
2675
|
const updatedSlices = await Promise.all(
|
2663
|
-
|
2676
|
+
args.sliceImages.map(async (sliceImage, index) => {
|
2664
2677
|
// ----- Q1 scope -----
|
2665
2678
|
|
2666
2679
|
console.log(
|
2667
|
-
"STEP
|
2680
|
+
"STEP 1: Generate the slice model using the image for slice:",
|
2668
2681
|
index,
|
2669
2682
|
);
|
2670
|
-
const updatedSlice = await generateSliceModel(
|
2683
|
+
const updatedSlice = await generateSliceModel(index, sliceImage);
|
2671
2684
|
|
2672
2685
|
console.log(
|
2673
|
-
"STEP
|
2686
|
+
"STEP 2: Persist the updated slice model for:",
|
2674
2687
|
`${index} - ${updatedSlice.name}`,
|
2675
2688
|
);
|
2676
2689
|
await this.updateSlice({
|
@@ -2679,7 +2692,7 @@ type GroupField = {
|
|
2679
2692
|
});
|
2680
2693
|
|
2681
2694
|
console.log(
|
2682
|
-
"STEP
|
2695
|
+
"STEP 3: Update the slice screenshot for:",
|
2683
2696
|
`${index} - ${updatedSlice.name}`,
|
2684
2697
|
);
|
2685
2698
|
await this.updateSliceScreenshot({
|
@@ -2694,11 +2707,15 @@ type GroupField = {
|
|
2694
2707
|
let updatedMock: SharedSliceContent[];
|
2695
2708
|
try {
|
2696
2709
|
console.log(
|
2697
|
-
"STEP
|
2710
|
+
"STEP 4: Generate updated mocks for:",
|
2698
2711
|
`${index} - ${updatedSlice.name}`,
|
2699
2712
|
);
|
2700
2713
|
const existingMocks = mockSlice({ model: updatedSlice });
|
2701
|
-
updatedMock = await generateSliceMocks(
|
2714
|
+
updatedMock = await generateSliceMocks(
|
2715
|
+
index,
|
2716
|
+
sliceImage,
|
2717
|
+
existingMocks,
|
2718
|
+
);
|
2702
2719
|
} catch (error) {
|
2703
2720
|
console.error(
|
2704
2721
|
`Failed to generate mocks for ${index} - ${updatedSlice.name}:`,
|
@@ -2710,26 +2727,22 @@ type GroupField = {
|
|
2710
2727
|
let componentCode: string | undefined;
|
2711
2728
|
try {
|
2712
2729
|
console.log(
|
2713
|
-
"STEP
|
2730
|
+
"STEP 5: Generate the isolated slice component code for:",
|
2714
2731
|
`${index} - ${updatedSlice.name}`,
|
2715
2732
|
);
|
2716
|
-
const globalStyle = await getGlobalStyle(
|
2717
|
-
`${folderPath}/globalStyle.css`,
|
2718
|
-
);
|
2719
2733
|
const initialCode = await generateSliceComponentCode(
|
2734
|
+
index,
|
2720
2735
|
sliceImage,
|
2721
|
-
codeFile,
|
2722
2736
|
updatedSlice,
|
2723
2737
|
);
|
2724
2738
|
|
2725
2739
|
console.log(
|
2726
|
-
"STEP
|
2740
|
+
"STEP 6: Generate the branding on the code:",
|
2727
2741
|
`${index} - ${updatedSlice.name}`,
|
2728
2742
|
);
|
2729
2743
|
componentCode = await generateSliceComponentCodeAppearance(
|
2744
|
+
index,
|
2730
2745
|
sliceImage,
|
2731
|
-
codeFile,
|
2732
|
-
globalStyle,
|
2733
2746
|
initialCode,
|
2734
2747
|
);
|
2735
2748
|
} catch (error) {
|
@@ -2744,40 +2757,49 @@ type GroupField = {
|
|
2744
2757
|
);
|
2745
2758
|
|
2746
2759
|
// Ensure to wait to have all slices code and mocks before writing on the disk
|
2747
|
-
await
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
|
2754
|
-
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2760
|
+
await updatedSlices.forEach(
|
2761
|
+
async ({ updatedSlice, componentCode, updatedMock }, index) => {
|
2762
|
+
console.log(
|
2763
|
+
"STEP 7: Update the slice code for:",
|
2764
|
+
`${index} - ${updatedSlice.name}`,
|
2765
|
+
);
|
2766
|
+
if (componentCode) {
|
2767
|
+
const { errors } = await this.createSlice({
|
2768
|
+
libraryID: DEFAULT_LIBRARY_ID,
|
2769
|
+
model: updatedSlice,
|
2770
|
+
componentContents: componentCode,
|
2771
|
+
});
|
2772
|
+
|
2773
|
+
if (errors.length > 0) {
|
2774
|
+
console.log(
|
2775
|
+
`Errors while updating the slice code for ${index} - ${updatedSlice.name}:`,
|
2776
|
+
errors,
|
2777
|
+
);
|
2761
2778
|
await this.createSlice({
|
2762
2779
|
libraryID: DEFAULT_LIBRARY_ID,
|
2763
2780
|
model: updatedSlice,
|
2764
2781
|
});
|
2765
2782
|
}
|
2766
|
-
|
2767
|
-
|
2768
|
-
"STEP 10: Persist the generated mocks for:",
|
2769
|
-
`${index} - ${updatedSlice.name}`,
|
2770
|
-
);
|
2771
|
-
await this.updateSliceMocks({
|
2783
|
+
} else {
|
2784
|
+
await this.createSlice({
|
2772
2785
|
libraryID: DEFAULT_LIBRARY_ID,
|
2773
|
-
|
2774
|
-
mocks: updatedMock,
|
2786
|
+
model: updatedSlice,
|
2775
2787
|
});
|
2776
|
-
}
|
2777
|
-
|
2788
|
+
}
|
2789
|
+
|
2790
|
+
console.log(
|
2791
|
+
"STEP 8: Persist the generated mocks for:",
|
2792
|
+
`${index} - ${updatedSlice.name}`,
|
2793
|
+
);
|
2794
|
+
await this.updateSliceMocks({
|
2795
|
+
libraryID: DEFAULT_LIBRARY_ID,
|
2796
|
+
sliceID: updatedSlice.id,
|
2797
|
+
mocks: updatedMock,
|
2798
|
+
});
|
2799
|
+
},
|
2778
2800
|
);
|
2779
2801
|
|
2780
|
-
console.log("STEP
|
2802
|
+
console.log("STEP 9: THE END");
|
2781
2803
|
|
2782
2804
|
return {
|
2783
2805
|
slices: updatedSlices.map(({ updatedSlice }) => updatedSlice),
|