Skip to content

Commit d773046

Browse files
authored
@W-21111039: Display Cartridge details (#166)
* display cartridge details
1 parent 94d2503 commit d773046

File tree

2 files changed

+226
-8
lines changed

2 files changed

+226
-8
lines changed

packages/b2c-vs-extension/src/extension.ts

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import {configureLogger} from '@salesforce/b2c-tooling-sdk/logging';
1010
import {findAndDeployCartridges, getActiveCodeVersion} from '@salesforce/b2c-tooling-sdk/operations/code';
1111
import {getPathKeys, type OpenApiSchemaInput} from '@salesforce/b2c-tooling-sdk/schemas';
1212
import {randomUUID} from 'node:crypto';
13+
import {exec} from 'child_process';
14+
import {promisify} from 'util';
15+
16+
const execAsync = promisify(exec);
1317

1418
/** Standard B2C Commerce WebDAV root directories. */
1519
const WEBDAV_ROOTS: Record<string, string> = {
@@ -27,6 +31,27 @@ import * as fs from 'fs';
2731
import * as path from 'path';
2832
import * as vscode from 'vscode';
2933

34+
/**
35+
* Recursively finds all files under dir whose names end with .json (metadata files).
36+
* Returns paths relative to dir.
37+
*/
38+
function findJsonFilesUnder(dir: string, baseDir: string = dir): string[] {
39+
const results: string[] = [];
40+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
41+
return results;
42+
}
43+
for (const name of fs.readdirSync(dir)) {
44+
const full = path.join(dir, name);
45+
const rel = path.relative(baseDir, full);
46+
if (fs.statSync(full).isDirectory()) {
47+
results.push(...findJsonFilesUnder(full, baseDir));
48+
} else if (name.endsWith('.json')) {
49+
results.push(rel);
50+
}
51+
}
52+
return results.sort();
53+
}
54+
3055
function getWebviewContent(context: vscode.ExtensionContext): string {
3156
const htmlPath = path.join(context.extensionPath, 'src', 'webview.html');
3257
return fs.readFileSync(htmlPath, 'utf-8');
@@ -1291,15 +1316,47 @@ function activateInner(context: vscode.ExtensionContext, log: vscode.OutputChann
12911316
"B2C DX: This command must be run under a Storefront Next storefront template. No 'cartridges' directory found.";
12921317
log.appendLine(`[Storefront Next Cartridge] ${message}`);
12931318
vscode.window.showErrorMessage(message);
1319+
panel.webview.postMessage({
1320+
type: 'createCartridgeResult',
1321+
generatedFiles: [],
1322+
error: message,
1323+
});
12941324
return;
12951325
}
12961326
const cmd = 'pnpm sfnext generate-cartridge -d .';
1297-
const term = vscode.window.createTerminal({
1298-
name: 'B2C Create Cartridge',
1299-
cwd: projectDirectory,
1300-
});
1301-
term.show();
1302-
term.sendText(cmd);
1327+
log.appendLine(`[Storefront Next Cartridge] Running: ${cmd}`);
1328+
panel.webview.postMessage({type: 'createCartridgeResult', generatedFiles: [], running: true});
1329+
try {
1330+
await execAsync(cmd, {cwd: projectDirectory, maxBuffer: 4 * 1024 * 1024});
1331+
const generatedFiles = findJsonFilesUnder(cartridgesDir).map((rel) =>
1332+
path.join('cartridges', rel).split(path.sep).join('/'),
1333+
);
1334+
log.appendLine(
1335+
`[Storefront Next Cartridge] Generated ${generatedFiles.length} metadata file(s):\n${generatedFiles.join('\n')}`,
1336+
);
1337+
panel.webview.postMessage({
1338+
type: 'createCartridgeResult',
1339+
generatedFiles,
1340+
error: undefined,
1341+
running: false,
1342+
});
1343+
vscode.window.showInformationMessage(
1344+
`B2C DX: Page Designer metadata generated. ${generatedFiles.length} file(s) under cartridges/.`,
1345+
);
1346+
} catch (err: unknown) {
1347+
const message = err instanceof Error ? err.message : String(err);
1348+
const stderr =
1349+
err && typeof err === 'object' && 'stderr' in err ? String((err as {stderr?: string}).stderr) : '';
1350+
const errorText = stderr || message;
1351+
log.appendLine(`[Storefront Next Cartridge] Generate failed: ${errorText}`);
1352+
panel.webview.postMessage({
1353+
type: 'createCartridgeResult',
1354+
generatedFiles: [],
1355+
error: errorText,
1356+
running: false,
1357+
});
1358+
vscode.window.showErrorMessage(`B2C DX: Generate Page Designer metadata failed. ${message}`);
1359+
}
13031360
} else if (msg.type === 'deployCartridge') {
13041361
const cartridgesDir = path.join(projectDirectory, 'cartridges');
13051362
if (!fs.existsSync(cartridgesDir) || !fs.statSync(cartridgesDir).isDirectory()) {
@@ -1347,17 +1404,32 @@ function activateInner(context: vscode.ExtensionContext, log: vscode.OutputChann
13471404
log.appendLine(
13481405
`[Storefront Next Cartridge] Deploying cartridges to ${instance.config.hostname} (${instance.config.codeVersion})...`,
13491406
);
1407+
panel.webview.postMessage({type: 'deployResult', running: true});
13501408
const result = await findAndDeployCartridges(instance, cartridgesDir, {});
1351-
console.log(result);
13521409
log.appendLine(
13531410
`[Storefront Next Cartridge] Deployed ${result.cartridges.length} cartridge(s) to ${result.codeVersion}.`,
13541411
);
1412+
panel.webview.postMessage({
1413+
type: 'deployResult',
1414+
success: true,
1415+
running: false,
1416+
hostname: instance.config.hostname,
1417+
codeVersion: result.codeVersion,
1418+
reloaded: result.reloaded,
1419+
cartridges: result.cartridges.map((c) => c.name),
1420+
});
13551421
vscode.window.showInformationMessage(
1356-
`B2C DX: Deployed ${result.cartridges.length} cartridge(s) to ${result.codeVersion}. ${result.cartridges.join(', ')} `,
1422+
`B2C DX: Deployed ${result.cartridges.length} cartridge(s) to ${result.codeVersion}. ${result.cartridges.map((c) => c.name).join(', ')} `,
13571423
);
13581424
} catch (err) {
13591425
const message = err instanceof Error ? err.message : String(err);
13601426
log.appendLine(`[Storefront Next Cartridge] Deploy failed: ${message}`);
1427+
panel.webview.postMessage({
1428+
type: 'deployResult',
1429+
success: false,
1430+
running: false,
1431+
error: message,
1432+
});
13611433
vscode.window.showErrorMessage(`B2C DX: Deploy failed. ${message}`);
13621434
}
13631435
}

packages/b2c-vs-extension/src/storefront-next-cartridge.html

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,56 @@
4040
.buttons button:hover {
4141
background: var(--vscode-button-hoverBackground);
4242
}
43+
.info {
44+
margin-top: 1.25rem;
45+
padding: 0.75rem;
46+
font-size: 0.9em;
47+
color: var(--vscode-descriptionForeground);
48+
background: var(--vscode-textBlockQuote-background);
49+
border-left: 4px solid var(--vscode-textBlockQuote-border);
50+
border-radius: 0 4px 4px 0;
51+
}
52+
.info h2 {
53+
font-size: 0.95em;
54+
margin: 0 0 0.5rem;
55+
color: var(--vscode-foreground);
56+
}
57+
.info ul {
58+
margin: 0.25rem 0 0 1rem;
59+
padding: 0;
60+
}
61+
.info code {
62+
font-size: 0.85em;
63+
}
64+
.info .status {
65+
margin: 0 0 0.5rem;
66+
}
67+
.info .status.error {
68+
color: var(--vscode-errorForeground);
69+
}
70+
.info .detail-rows {
71+
margin-top: 0.5rem;
72+
}
73+
.info .detail-row {
74+
display: flex;
75+
gap: 0.5rem;
76+
margin-bottom: 0.25rem;
77+
font-size: 0.9em;
78+
}
79+
.info .detail-label {
80+
flex: 0 0 7rem;
81+
color: var(--vscode-descriptionForeground);
82+
}
83+
.info .detail-value {
84+
word-break: break-all;
85+
}
86+
.info .error-detail {
87+
color: var(--vscode-errorForeground);
88+
margin: 0.25rem 0 0;
89+
}
90+
.hidden {
91+
display: none !important;
92+
}
4393
</style>
4494
</head>
4595
<body>
@@ -48,15 +98,111 @@ <h1>Storefront Next Cartridge</h1>
4898
<button type="button" id="create-cartridge-btn">Generate PageDesigner Metadata</button>
4999
<button type="button" id="deploy-cartridge-btn">Deploy Cartridge</button>
50100
</div>
101+
<div id="generated-section" class="info hidden">
102+
<h2>Generated metadata files</h2>
103+
<p id="generated-status" class="status"></p>
104+
<ul id="generated-files-list"></ul>
105+
</div>
106+
<div id="deploy-section" class="info hidden">
107+
<h2>Deploy result</h2>
108+
<p id="deploy-status" class="status"></p>
109+
<div id="deploy-details"></div>
110+
</div>
51111
<script>
52112
(function () {
53113
const vscode = acquireVsCodeApi();
114+
const generatedSection = document.getElementById('generated-section');
115+
const generatedStatus = document.getElementById('generated-status');
116+
const generatedList = document.getElementById('generated-files-list');
117+
const createBtn = document.getElementById('create-cartridge-btn');
118+
const deploySection = document.getElementById('deploy-section');
119+
const deployStatus = document.getElementById('deploy-status');
120+
const deployDetails = document.getElementById('deploy-details');
121+
const deployBtn = document.getElementById('deploy-cartridge-btn');
122+
123+
function escapeHtml(s) {
124+
const div = document.createElement('div');
125+
div.textContent = s;
126+
return div.innerHTML;
127+
}
128+
54129
document.getElementById('create-cartridge-btn').addEventListener('click', function () {
55130
vscode.postMessage({type: 'createCartridge'});
56131
});
57132
document.getElementById('deploy-cartridge-btn').addEventListener('click', function () {
58133
vscode.postMessage({type: 'deployCartridge'});
59134
});
135+
136+
window.addEventListener('message', function (event) {
137+
const msg = event.data;
138+
if (msg.type === 'createCartridgeResult') {
139+
generatedSection.classList.remove('hidden');
140+
if (msg.running) {
141+
generatedStatus.textContent = 'Running generate-cartridge…';
142+
generatedList.innerHTML = '';
143+
createBtn.disabled = true;
144+
return;
145+
}
146+
createBtn.disabled = false;
147+
if (msg.error) {
148+
generatedStatus.textContent = 'Error: ' + msg.error;
149+
generatedStatus.classList.add('error');
150+
generatedList.innerHTML = '';
151+
return;
152+
}
153+
generatedStatus.classList.remove('error');
154+
generatedStatus.textContent = msg.generatedFiles.length
155+
? 'Generated ' + msg.generatedFiles.length + ' file(s):'
156+
: 'No .json metadata files found under cartridges/';
157+
generatedList.innerHTML = msg.generatedFiles
158+
.map(function (f) {
159+
return '<li><code>' + escapeHtml(f) + '</code></li>';
160+
})
161+
.join('');
162+
return;
163+
}
164+
if (msg.type === 'deployResult') {
165+
deploySection.classList.remove('hidden');
166+
if (msg.running) {
167+
deployStatus.textContent = 'Deploying cartridges…';
168+
deployStatus.classList.remove('error');
169+
deployDetails.innerHTML = '';
170+
deployBtn.disabled = true;
171+
return;
172+
}
173+
deployBtn.disabled = false;
174+
if (msg.error) {
175+
deployStatus.textContent = 'Deploy failed.';
176+
deployStatus.classList.add('error');
177+
deployDetails.innerHTML = '<p class="error-detail">' + escapeHtml(msg.error) + '</p>';
178+
return;
179+
}
180+
deployStatus.classList.remove('error');
181+
deployStatus.textContent = 'Deployed successfully.';
182+
var html = '<div class="detail-rows">';
183+
html +=
184+
'<div class="detail-row"><span class="detail-label">Hostname:</span><span class="detail-value">' +
185+
escapeHtml(msg.hostname || '—') +
186+
'</span></div>';
187+
html +=
188+
'<div class="detail-row"><span class="detail-label">Code version:</span><span class="detail-value">' +
189+
escapeHtml(msg.codeVersion || '—') +
190+
'</span></div>';
191+
html +=
192+
'<div class="detail-row"><span class="detail-label">Reloaded:</span><span class="detail-value">' +
193+
(msg.reloaded ? 'Yes' : 'No') +
194+
'</span></div>';
195+
html +=
196+
'<div class="detail-row"><span class="detail-label">Cartridges:</span><span class="detail-value">' +
197+
(msg.cartridges && msg.cartridges.length ? escapeHtml(msg.cartridges.join(', ')) : '—') +
198+
'</span></div>';
199+
html += '</div>';
200+
if (msg.cartridges && msg.cartridges.length > 0) {
201+
html += '<p style="margin-top: 0.5rem;">Count: ' + msg.cartridges.length + ' cartridge(s)</p>';
202+
}
203+
deployDetails.innerHTML = html;
204+
}
205+
});
60206
})();
61207
</script>
62208
</body>

0 commit comments

Comments
 (0)