@lwc/engine-core 8.2.0 → 8.4.0

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/README.md CHANGED
@@ -115,3 +115,9 @@ This experimental API enables the sanitization of HTML content by external servi
115
115
  ### unwrap()
116
116
 
117
117
  This experimental API enables the removal of an object's observable membrane proxy wrapper.
118
+
119
+ ### setTrustedSignalSet()
120
+
121
+ This experimental API enables the addition of a signal as a trusted signal. If the [ENABLE_EXPERIMENTAL_SIGNALS](https://github.com/salesforce/lwc/blob/master/packages/%40lwc/features/README.md#lwcfeatures) feature is enabled, any signal value change will trigger a re-render.
122
+
123
+ If `setTrustedSignalSet` is called more than once, it will throw an error. If it is never called, then no trusted signal validation will be performed. The same `setTrustedSignalSet` API must be called on both `@lwc/engine-dom` and `@lwc/signals`.
@@ -28,3 +28,4 @@ export { default as track } from './decorators/track';
28
28
  export { default as wire } from './decorators/wire';
29
29
  export { readonly } from './readonly';
30
30
  export { setFeatureFlag, setFeatureFlagForTest } from '@lwc/features';
31
+ export { setTrustedSignalSet } from '@lwc/shared';
@@ -1,4 +1,4 @@
1
- import { ShadowMode, ShadowSupportMode } from './vm';
1
+ import { RenderMode, ShadowMode, ShadowSupportMode } from './vm';
2
2
  export declare const enum ReportingEventId {
3
3
  CrossRootAriaInSyntheticShadow = "CrossRootAriaInSyntheticShadow",
4
4
  CompilerRuntimeVersionMismatch = "CompilerRuntimeVersionMismatch",
@@ -7,7 +7,8 @@ export declare const enum ReportingEventId {
7
7
  StylesheetMutation = "StylesheetMutation",
8
8
  ConnectedCallbackWhileDisconnected = "ConnectedCallbackWhileDisconnected",
9
9
  ShadowModeUsage = "ShadowModeUsage",
10
- ShadowSupportModeUsage = "ShadowSupportModeUsage"
10
+ ShadowSupportModeUsage = "ShadowSupportModeUsage",
11
+ RenderModeMismatch = "RenderModeMismatch"
11
12
  }
12
13
  export interface BasePayload {
13
14
  tagName?: string;
@@ -32,6 +33,9 @@ export interface StylesheetMutationPayload extends BasePayload {
32
33
  }
33
34
  export interface ConnectedCallbackWhileDisconnectedPayload extends BasePayload {
34
35
  }
36
+ export interface RenderModeMismatchPayload extends BasePayload {
37
+ mode: RenderMode;
38
+ }
35
39
  export interface ShadowModeUsagePayload extends BasePayload {
36
40
  mode: ShadowMode;
37
41
  }
@@ -47,6 +51,7 @@ export type ReportingPayloadMapping = {
47
51
  [ReportingEventId.ConnectedCallbackWhileDisconnected]: ConnectedCallbackWhileDisconnectedPayload;
48
52
  [ReportingEventId.ShadowModeUsage]: ShadowModeUsagePayload;
49
53
  [ReportingEventId.ShadowSupportModeUsage]: ShadowSupportModeUsagePayload;
54
+ [ReportingEventId.RenderModeMismatch]: RenderModeMismatchPayload;
50
55
  };
51
56
  export type ReportingDispatcher<T extends ReportingEventId = ReportingEventId> = (reportingEventId: T, payload: ReportingPayloadMapping[T]) => void;
52
57
  /** Callbacks to invoke when reporting is enabled */
package/dist/index.cjs.js CHANGED
@@ -296,6 +296,16 @@ function toPrettyMemberNotation(parent, child) {
296
296
  return `${shared.toString(parent)}[${JSON.stringify(child)}]`;
297
297
  }
298
298
  }
299
+ function safelyCallGetter(target, key) {
300
+ // Arbitrary getters can throw. We don't want to throw an error just due to dev-mode-only mutation tracking
301
+ // (which is only used for performance debugging) so ignore errors here.
302
+ try {
303
+ return target[key];
304
+ }
305
+ catch (_err) {
306
+ /* ignore */
307
+ }
308
+ }
299
309
  /**
300
310
  * Flush all the logs we've written so far and return the current logs.
301
311
  */
@@ -364,7 +374,7 @@ function trackTargetForMutationLogging(key, target) {
364
374
  // Deeply traverse arrays and objects to track every object within
365
375
  if (shared.isArray(target)) {
366
376
  for (let i = 0; i < target.length; i++) {
367
- trackTargetForMutationLogging(toPrettyMemberNotation(key, i), target[i]);
377
+ trackTargetForMutationLogging(toPrettyMemberNotation(key, i), safelyCallGetter(target, i));
368
378
  }
369
379
  }
370
380
  else {
@@ -373,10 +383,10 @@ function trackTargetForMutationLogging(key, target) {
373
383
  // https://github.com/salesforce/observable-membrane/blob/b85417f/src/base-handler.ts#L142-L143
374
384
  // Note this code path is very hot, hence doing two separate for-loops rather than creating a new array.
375
385
  for (const prop of shared.getOwnPropertyNames(target)) {
376
- trackTargetForMutationLogging(toPrettyMemberNotation(key, prop), target[prop]);
386
+ trackTargetForMutationLogging(toPrettyMemberNotation(key, prop), safelyCallGetter(target, prop));
377
387
  }
378
388
  for (const prop of shared.getOwnPropertySymbols(target)) {
379
- trackTargetForMutationLogging(toPrettyMemberNotation(key, prop), target[prop]);
389
+ trackTargetForMutationLogging(toPrettyMemberNotation(key, prop), safelyCallGetter(target, prop));
380
390
  }
381
391
  }
382
392
  }
@@ -602,6 +612,7 @@ function componentValueObserved(vm, key, target = {}) {
602
612
  'value' in target &&
603
613
  'subscribe' in target &&
604
614
  shared.isFunction(target.subscribe) &&
615
+ shared.isTrustedSignal(target) &&
605
616
  // Only subscribe if a template is being rendered by the engine
606
617
  tro.isObserving()) {
607
618
  // Subscribe the template reactive observer's notify method, which will mark the vm as dirty and schedule hydration.
@@ -668,8 +679,9 @@ for (const [propName, attrName] of shared.entries(shared.AriaPropNameToAttrNameM
668
679
  return this.getAttribute(attrName);
669
680
  },
670
681
  set(newValue) {
671
- // TODO [#3284]: there is disagreement between browsers and the spec on how to treat undefined
672
- // Our historical behavior is to only treat null as removing the attribute
682
+ // TODO [#3284]: According to the spec, IDL nullable type values
683
+ // (null and undefined) should remove the attribute; however, we
684
+ // only do so in the case of null for historical reasons.
673
685
  // See also https://github.com/w3c/aria/issues/1858
674
686
  if (shared.isNull(newValue)) {
675
687
  this.removeAttribute(attrName);
@@ -6182,19 +6194,30 @@ function validateSlots(vm) {
6182
6194
  shared.assert.isTrue(shared.isArray(cmpSlots.slotAssignments[slotName]), `Slots can only be set to an array, instead received ${shared.toString(cmpSlots.slotAssignments[slotName])} for slot "${slotName}" in ${vm}.`);
6183
6195
  }
6184
6196
  }
6185
- function validateLightDomTemplate(template, vm) {
6186
- assertNotProd(); // should never leak to prod mode
6187
- if (template === defaultEmptyTemplate) {
6197
+ function checkHasMatchingRenderMode(template, vm) {
6198
+ // don't validate in prod environments where reporting is disabled
6199
+ if (process.env.NODE_ENV === 'production' && !isReportingEnabled()) {
6188
6200
  return;
6189
6201
  }
6190
- if (vm.renderMode === 0 /* RenderMode.Light */) {
6191
- if (template.renderMode !== 'light') {
6192
- 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)}.`);
6193
- }
6202
+ // don't validate the default empty template - it is not inherently light or shadow
6203
+ if (template === defaultEmptyTemplate) {
6204
+ return;
6194
6205
  }
6195
- else {
6196
- if (!shared.isUndefined(template.renderMode)) {
6197
- 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"`);
6206
+ // TODO [#4663]: `renderMode` mismatch between template and component causes `console.error` but no error
6207
+ // Note that `undefined` means shadow in this case, because shadow is the default.
6208
+ const vmIsLight = vm.renderMode === 0 /* RenderMode.Light */;
6209
+ const templateIsLight = template.renderMode === 'light';
6210
+ if (vmIsLight !== templateIsLight) {
6211
+ report("RenderModeMismatch" /* ReportingEventId.RenderModeMismatch */, {
6212
+ tagName: vm.tagName,
6213
+ mode: vm.renderMode,
6214
+ });
6215
+ if (process.env.NODE_ENV !== 'production') {
6216
+ const tagName = getComponentTag(vm);
6217
+ const message = vmIsLight
6218
+ ? `Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive to the root template tag of ${tagName}.`
6219
+ : `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"`;
6220
+ logError(message);
6198
6221
  }
6199
6222
  }
6200
6223
  }
@@ -6409,9 +6432,7 @@ function evaluateTemplate(vm, html) {
6409
6432
  if (!isTemplateRegistered(html)) {
6410
6433
  throw new TypeError(`Invalid template returned by the render() method on ${vm.tagName}. It must return an imported template (e.g.: \`import html from "./${vm.def.name}.html"\`), instead, it has returned: ${shared.toString(html)}.`);
6411
6434
  }
6412
- if (process.env.NODE_ENV !== 'production') {
6413
- validateLightDomTemplate(html, vm);
6414
- }
6435
+ checkHasMatchingRenderMode(html, vm);
6415
6436
  // Perf opt: do not reset the shadow root during the first rendering (there is
6416
6437
  // nothing to reset).
6417
6438
  if (!shared.isNull(cmpTemplate)) {
@@ -7998,7 +8019,10 @@ function validateClassAttr(vnode, elm, data, renderer) {
7998
8019
  let nodesAreCompatible = true;
7999
8020
  let readableVnodeClassname;
8000
8021
  const elmClassName = getAttribute(elm, 'class');
8001
- if (!shared.isUndefined(className) && String(className) !== elmClassName) {
8022
+ if (!shared.isUndefined(className) &&
8023
+ String(className) !== elmClassName &&
8024
+ // No mismatch if SSR `class` attribute is missing and CSR `class` is the empty string
8025
+ !(className === '' && shared.isNull(elmClassName))) {
8002
8026
  // className is used when class is bound to an expr.
8003
8027
  nodesAreCompatible = false;
8004
8028
  // stringify for pretty-printing
@@ -8137,13 +8161,15 @@ function haveCompatibleStaticParts(vnode, renderer) {
8137
8161
  // Explicitly skip hydration validation when static parts don't contain `style` or `className`.
8138
8162
  // This means the style/class attributes are either static or don't exist on the element and
8139
8163
  // cannot be affected by hydration.
8140
- const hasMatchingStyleAttr = shouldValidateAttr(data, 'style')
8141
- ? validateStyleAttr(vnode, elm, data, renderer)
8142
- : true;
8164
+ // We need to do class first, style second to match the ordering of non-static-optimized nodes,
8165
+ // otherwise the ordering of console errors is different between the two.
8143
8166
  const hasMatchingClass = shouldValidateAttr(data, 'className')
8144
8167
  ? validateClassAttr(vnode, elm, data, renderer)
8145
8168
  : true;
8146
- if (shared.isFalse(hasMatchingAttrs && hasMatchingStyleAttr && hasMatchingClass)) {
8169
+ const hasMatchingStyleAttr = shouldValidateAttr(data, 'style')
8170
+ ? validateStyleAttr(vnode, elm, data, renderer)
8171
+ : true;
8172
+ if (shared.isFalse(hasMatchingAttrs && hasMatchingClass && hasMatchingStyleAttr)) {
8147
8173
  return false;
8148
8174
  }
8149
8175
  }
@@ -8433,6 +8459,10 @@ function readonly(obj) {
8433
8459
  return getReadOnlyProxy(obj);
8434
8460
  }
8435
8461
 
8462
+ Object.defineProperty(exports, "setTrustedSignalSet", {
8463
+ enumerable: true,
8464
+ get: function () { return shared.setTrustedSignalSet; }
8465
+ });
8436
8466
  Object.defineProperty(exports, "setFeatureFlag", {
8437
8467
  enumerable: true,
8438
8468
  get: function () { return features.setFeatureFlag; }
@@ -8477,5 +8507,5 @@ exports.swapTemplate = swapTemplate;
8477
8507
  exports.track = track;
8478
8508
  exports.unwrap = unwrap;
8479
8509
  exports.wire = wire;
8480
- /** version: 8.2.0 */
8510
+ /** version: 8.4.0 */
8481
8511
  //# sourceMappingURL=index.cjs.js.map