Skip to content

Commit

Permalink
chore(engine): report renderMode mismatch (#4667)
Browse files Browse the repository at this point in the history
  • Loading branch information
nolanlawson authored Oct 21, 2024
1 parent 5391bc3 commit f005927
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 22 deletions.
8 changes: 7 additions & 1 deletion packages/@lwc/engine-core/src/framework/reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { noop } from '@lwc/shared';

import { ShadowMode, ShadowSupportMode } from './vm';
import { RenderMode, ShadowMode, ShadowSupportMode } from './vm';

export const enum ReportingEventId {
CrossRootAriaInSyntheticShadow = 'CrossRootAriaInSyntheticShadow',
Expand All @@ -17,6 +17,7 @@ export const enum ReportingEventId {
ConnectedCallbackWhileDisconnected = 'ConnectedCallbackWhileDisconnected',
ShadowModeUsage = 'ShadowModeUsage',
ShadowSupportModeUsage = 'ShadowSupportModeUsage',
RenderModeMismatch = 'RenderModeMismatch',
}

export interface BasePayload {
Expand Down Expand Up @@ -49,6 +50,10 @@ export interface StylesheetMutationPayload extends BasePayload {
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface ConnectedCallbackWhileDisconnectedPayload extends BasePayload {}

export interface RenderModeMismatchPayload extends BasePayload {
mode: RenderMode;
}

export interface ShadowModeUsagePayload extends BasePayload {
mode: ShadowMode;
}
Expand All @@ -68,6 +73,7 @@ export type ReportingPayloadMapping = {
[ReportingEventId.ConnectedCallbackWhileDisconnected]: ConnectedCallbackWhileDisconnectedPayload;
[ReportingEventId.ShadowModeUsage]: ShadowModeUsagePayload;
[ReportingEventId.ShadowSupportModeUsage]: ShadowSupportModeUsagePayload;
[ReportingEventId.RenderModeMismatch]: RenderModeMismatchPayload;
};

export type ReportingDispatcher<T extends ReportingEventId = ReportingEventId> = (
Expand Down
44 changes: 24 additions & 20 deletions packages/@lwc/engine-core/src/framework/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { MutableVNodes, VNodes, VStaticPart, VStaticPartElement, VStaticPartText
import { RendererAPI } from './renderer';
import { getMapFromClassName } from './modules/computed-class-attr';
import { FragmentCacheKey, getFromFragmentCache, setInFragmentCache } from './fragment-cache';
import { isReportingEnabled, report, ReportingEventId } from './reporting';

export interface Template {
(api: RenderAPI, cmp: object, slotSet: SlotSet, cache: TemplateCache): VNodes;
Expand Down Expand Up @@ -99,26 +100,31 @@ function validateSlots(vm: VM) {
}
}

function validateLightDomTemplate(template: Template, vm: VM) {
assertNotProd(); // should never leak to prod mode
function checkHasMatchingRenderMode(template: Template, vm: VM) {
// don't validate in prod environments where reporting is disabled
if (process.env.NODE_ENV === 'production' && !isReportingEnabled()) {
return;
}
// don't validate the default empty template - it is not inherently light or shadow
if (template === defaultEmptyTemplate) {
return;
}
if (vm.renderMode === RenderMode.Light) {
if (template.renderMode !== 'light') {
logError(
`Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive to the root template tag of ${getComponentTag(
vm
)}.`
);
}
} else {
if (!isUndefined(template.renderMode)) {
logError(
`Shadow DOM components template can't render light DOM templates. Either remove the 'lwc:render-mode' directive from ${getComponentTag(
vm
)} or set it to 'lwc:render-mode="shadow"`
);
// TODO [#4663]: `renderMode` mismatch between template and component causes `console.error` but no error
// Note that `undefined` means shadow in this case, because shadow is the default.
const vmIsLight = vm.renderMode === RenderMode.Light;
const templateIsLight = template.renderMode === 'light';
if (vmIsLight !== templateIsLight) {
report(ReportingEventId.RenderModeMismatch, {
tagName: vm.tagName,
mode: vm.renderMode,
});
if (process.env.NODE_ENV !== 'production') {
const tagName = getComponentTag(vm);
const message = vmIsLight
? `Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive to the root template tag of ${tagName}.`
: `Shadow DOM components template can't render light DOM templates. Either remove the 'lwc:render-mode' directive from ${tagName} or set it to 'lwc:render-mode="shadow"`;

logError(message);
}
}
}
Expand Down Expand Up @@ -390,9 +396,7 @@ export function evaluateTemplate(vm: VM, html: Template): VNodes {
);
}

if (process.env.NODE_ENV !== 'production') {
validateLightDomTemplate(html, vm);
}
checkHasMatchingRenderMode(html, vm);

// Perf opt: do not reset the shadow root during the first rendering (there is
// nothing to reset).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import { createElement } from 'lwc';

import { attachReportingControlDispatcher, detachReportingControlDispatcher } from 'test-utils';
import Shadow from 'x/shadow';
import Light from 'x/light';

describe('lwc:render-mode', () => {
let dispatcher;

beforeEach(() => {
dispatcher = jasmine.createSpy();
attachReportingControlDispatcher(dispatcher, ['RenderModeMismatch']);
});

afterEach(() => {
detachReportingControlDispatcher();
});

it('should throw error if shadow template is passed to light component', () => {
expect(() => {
const root = createElement('x-test', { is: Light });
document.body.appendChild(root);
}).toLogErrorDev(
/Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive to the root template tag of <x-test>./
);

expect(dispatcher.calls.allArgs()).toEqual([
[
'RenderModeMismatch',
{
tagName: 'x-test',
mode: 0, // RenderMode.Light
},
],
]);
});
it('should throw error if light template is passed to shadow component', () => {
expect(() => {
Expand All @@ -19,5 +40,15 @@ describe('lwc:render-mode', () => {
}).toLogErrorDev(
/Shadow DOM components template can't render light DOM templates. Either remove the 'lwc:render-mode' directive from <x-test> or set it to 'lwc:render-mode="shadow"/
);

expect(dispatcher.calls.allArgs()).toEqual([
[
'RenderModeMismatch',
{
tagName: 'x-test',
mode: 1, // RenderMode.Shadow
},
],
]);
});
});

0 comments on commit f005927

Please sign in to comment.