Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.title {
font-weight: 700;
font-size: 14px;
color: #c23934;
margin-bottom: 8px;
}
.kv { margin-bottom: 12px; }
.kv-row { display: flex; gap: 8px; margin: 2px 0; font-size: 12px; }
.kv-label { width: 90px; color: #6b6d70; font-weight: 600; }
.kv-value { color: #080707; }
.meta {
display: flex;
gap: 6px;
margin-bottom: 12px;
}
.pill {
background: #f3f2f2;
border-radius: 10px;
padding: 2px 8px;
font-size: 12px;
}
.stack {
background: #fffbe6;
border: 1px solid #ece0a1;
padding: 8px;
margin-bottom: 12px;
max-height: 180px;
overflow: auto;
}
.stack.empty {
color: #6b6d70;
background: #fafafa;
border: 1px dashed #d8dde6;
}
.section-title {
font-weight: 600;
font-size: 12px;
color: #3e3e3c;
margin: 6px 0 4px;
}
.stack-line {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 12px;
white-space: pre-wrap;
}
.props-title {
font-weight: 600;
margin-bottom: 4px;
}
.prop-row {
display: flex;
gap: 8px;
font-size: 12px;
}
.k { color: #6b6d70; }
.v { color: #080707; }
.actions { margin-top: 12px; }


Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<lightning-card title="Preview Error" icon-name="utility:warning">
<div class="slds-p-around_medium">
<div class="title">{view.title}</div>
<div class="section-title">Details</div>
<div class="kv">
<div class="kv-row">
<span class="kv-label">Message</span>
<span class="kv-value">{view.title}</span>
</div>
<div class="kv-row">
<span class="kv-label">Error ID</span>
<span class="kv-value">{view.errorId}</span>
</div>
<div class="kv-row">
<span class="kv-label">Component</span>
<span class="kv-value">{view.componentName}</span>
</div>
<div class="kv-row">
<span class="kv-label">Timestamp</span>
<span class="kv-value">{view.timestamp}</span>
</div>
</div>
<div class="section-title">Stack Trace</div>
<template if:true={view.stackLines}>
<div class="stack">
<template for:each={view.stackLines} for:item="line">
<div key={line} class="stack-line">{line}</div>
</template>
</div>
</template>
<template if:false={view.stackLines}>
<div class="stack empty">No stack trace available.</div>
</template>
<template if:true={hasProps}>
<div class="props">
<div class="props-title">Props</div>
<template for:each={propEntries} for:item="entry">
<div key={entry.key} class="prop-row">
<span class="k">{entry.key}</span>
<span class="v">{entry.value}</span>
</div>
</template>
</div>
</template>
<div class="actions">
<lightning-button variant="brand" label="Copy Details" onclick={handleCopy}></lightning-button>
</div>
</div>
</lightning-card>
</template>


Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { LightningElement, api, track } from 'lwc';

export default class ErrorDisplay extends LightningElement {
_payload;
@track propEntriesInternal = [];
@track view = { title: 'No error payload provided.', errorId: 'LWC-Runtime-EID-DEMO', timestamp: '', componentName: 'c-unknown', stackLines: [] };

@api
get payload() {
return this._payload;
}
set payload(value) {
this._payload = value;
this.computeView();
this.normalizeProps();
}

connectedCallback() {
// Provide a visible default so demos render even if parent forgets to pass a payload
if (!this._payload) {
// Default to the new wrapper format with one error sample
this._payload = {
success: true,
count: 1,
errors: [
{
errorId: 'a1b2c3d4-e5f6-4a1b-8c9d-0e1f2a3b4c5d',
timestamp: '2025-10-21T10:30:45.123Z',
error: {
name: 'ReferenceError',
message: 'nonExistentMethod is not defined',
stack: 'ReferenceError: nonExistentMethod is not defined\n at ErrorTestComponent.connectedCallback...',
sanitizedStack: [
{
functionName: 'connectedCallback',
fileName: 'errorTestComponent.js',
lineNumber: 5,
columnNumber: 10,
isLocalSource: true,
},
],
},
component: {
name: 'c-error-test-component',
namespace: 'c',
tagName: 'c-error-test-component',
lifecycle: 'connectedCallback',
filePath: null,
},
runtime: {
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...',
viewport: { width: 1920, height: 1080 },
url: 'https://myorg.lightning.force.com/...',
lwcVersion: null,
isDevelopment: true,
},
state: { props: { recordId: '001xx000003DHP0' }, publicProperties: ['recordId', 'objectApiName'], isConnected: true },
source: { fileName: 'errorTestComponent.js', lineNumber: 5, columnNumber: 10 },
metadata: { severity: 'error', wasHandled: false, occurrenceCount: 1, tags: ['runtime', 'browser', 'lwc'] },
},
],
};
}
this.computeView();
this.normalizeProps();
}

computeView() {
try {
let title = 'Unknown error';
let errorId = 'N/A';
let timestamp = '';
let componentName = 'c-unknown';
let stackLines = [];

if (this._payload && Array.isArray(this._payload.errors) && this._payload.errors.length > 0) {
const e = this._payload.errors[0];
title = (e && e.error && (e.error.message || e.error.name)) || title;
errorId = e && e.errorId ? e.errorId : errorId;
timestamp = e && e.timestamp ? e.timestamp : '';
componentName = (e && e.component && (e.component.name || e.component.tagName)) || componentName;

if (e && e.error && Array.isArray(e.error.sanitizedStack) && e.error.sanitizedStack.length) {
stackLines = e.error.sanitizedStack.map((f) => {
const fn = f.functionName || '<anonymous>';
const file = f.fileName || 'unknown';
const line = f.lineNumber != null ? f.lineNumber : '?';
const col = f.columnNumber != null ? f.columnNumber : '?';
return `${fn} (${file}:${line}:${col})`;
});
} else if (e && e.error && e.error.stack) {
stackLines = String(e.error.stack)
.split('\n')
.map((l) => l.trim())
.filter(Boolean)
.slice(0, 30);
}

// also set props for normalizeProps()
this._propsForView = (e && e.state && e.state.props) || {};
} else if (this._payload) {
// Back-compat with older simple shape
title = this._payload.errorMessage || title;
errorId = this._payload.errorId || errorId;
timestamp = this._payload.timestamp || '';
componentName = this._payload.componentName || componentName;
stackLines = Array.isArray(this._payload.stackTrace) ? this._payload.stackTrace : [];
this._propsForView = this._payload.props || {};
}

this.view = { title, errorId, timestamp, componentName, stackLines };
} catch (_) {
// keep default view
}
}

normalizeProps() {
const p = this._propsForView || {};
if (!p) {
this.propEntriesInternal = [];
return;
}
const entries = [];
for (const k of Object.keys(p)) {
let v = p[k];
try {
if (typeof v === 'object') v = JSON.stringify(v);
} catch (e) {
// ignore stringify errors
}
entries.push({ key: k, value: String(v) });
}
// Only assign if changed to avoid unnecessary re-renders
const prev = this.propEntriesInternal || [];
const sameLength = prev.length === entries.length;
const samePairs = sameLength && prev.every((it, i) => it.key === entries[i].key && it.value === entries[i].value);
if (!samePairs) {
this.propEntriesInternal = entries;
}
}

get hasProps() {
return this.propEntriesInternal && this.propEntriesInternal.length > 0;
}

get propEntries() {
return this.propEntriesInternal;
}

async handleCopy() {
const text = JSON.stringify(this._payload || {}, null, 2);
try {
await navigator.clipboard.writeText(text);
} catch (e) {
// fallback
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.focus();
ta.select();
try {
document.execCommand('copy');
} catch (_) {
// ignore
}
document.body.removeChild(ta);
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.important-text {
font-weight: bold;
color: blue;
color: rgb(190, 30, 121);
padding-bottom: 10px;
}

.footer-text {
font-style: italic;
color: grey;
color: rgb(193, 12, 12);
font-size: 0.8rem;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template>
<lightning-card title="HMR Test Component - Enhanced" icon-name="custom:custom14">
<lightning-card title="HMR Test Component - ok" icon-name="custom:custom14">
<div class="slds-m-around_medium">
<p class="important-text">Hello from HMR Test!</p>
<p>Current Count: {counter}</p>
<lightning-button label="Increment" onclick="{handleIncrement}"></lightning-button>
<lightning-button label="Increment" onclick={handleIncrement}></lightning-button>
<br /><br />
<lightning-badge label="HMR Active"></lightning-badge>
<br /><br />
<lightning-input label="Sample Input" placeholder="Type something..."></lightning-input>
<br /><br />
<div class="slds-text-color_weak">Env → Browser: {env.isBrowser} | VS Code: {env.isVSCode} | Origin: {env.origin}</div>
</div>
<p class="footer-text slds-m-around_medium">Some text to modify for HMR.</p>
</lightning-card>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { LightningElement, track } from 'lwc';
import { isVSCodeExtension, isBrowser, getOriginSafe } from 'c/runtimeEnv';

export default class HmrTest extends LightningElement {
@track counter = 0;
@track env = { isVSCode: false, isBrowser: true, origin: '' };

handleIncrement() {
this.counter++;
}

connectedCallback() {
// Verify runtimeEnv module import
// eslint-disable-next-line no-console
console.log('[runtimeEnv]', {
isVSCode: isVSCodeExtension(),
isBrowser: isBrowser(),
origin: getOriginSafe(),
});

// Update on-screen env status for quick verification
this.env = {
isVSCode: isVSCodeExtension(),
isBrowser: isBrowser(),
origin: getOriginSafe(),
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<!-- Renderless utility component -->
</template>


Loading
Loading