@lwc/engine-core 8.0.0-alpha.1 → 8.1.0-alpha.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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2024 Salesforce, Inc.
3
3
  */
4
- import { noop, StringToLowerCase, isNull, ArrayPush as ArrayPush$1, ArrayJoin, isFrozen, isUndefined as isUndefined$1, defineProperty, ArrayIndexOf, ArrayPop, create, isFalse, isFunction as isFunction$1, isObject, seal, isAPIFeatureEnabled, isArray as isArray$1, keys, hasOwnProperty as hasOwnProperty$1, entries, AriaPropNameToAttrNameMap, getPropertyDescriptor, forEach, defineProperties, getPrototypeOf as getPrototypeOf$1, setPrototypeOf, assign, assert, freeze, KEY__SYNTHETIC_MODE, toString as toString$1, getOwnPropertyDescriptor as getOwnPropertyDescriptor$1, LWC_VERSION_COMMENT_REGEX, LWC_VERSION, getOwnPropertyNames as getOwnPropertyNames$1, getOwnPropertyDescriptors, htmlPropertyToAttribute, ArraySlice, ArrayMap, KEY__SCOPED_CSS, ArraySplice, kebabCaseToCamelCase, StringCharCodeAt, XML_NAMESPACE, XLINK_NAMESPACE, isString, StringSlice, isTrue, SVG_NAMESPACE, KEY__SHADOW_STATIC, KEY__SHADOW_RESOLVER, ArraySome, isNumber, StringReplace, StringTrim, ArrayFrom, htmlEscape, StringCharAt, ArrayUnshift, LOWEST_API_VERSION, KEY__NATIVE_GET_ELEMENT_BY_ID, KEY__NATIVE_QUERY_SELECTOR_ALL, ID_REFERENCING_ATTRIBUTES_SET, KEY__SHADOW_TOKEN, ArrayFilter, StringSplit, arrayEvery, ArrayIncludes, ArrayCopyWithin, ArrayFill, ArraySort, ArrayReverse, ArrayShift } from '@lwc/shared';
4
+ import { noop, StringToLowerCase, isNull, ArrayPush as ArrayPush$1, ArrayJoin, isFrozen, isUndefined as isUndefined$1, defineProperty, seal, create, isAPIFeatureEnabled, isArray as isArray$1, isFunction as isFunction$1, keys, ArrayFilter, isObject, toString as toString$1, ArrayIndexOf, ArrayPop, isFalse, hasOwnProperty as hasOwnProperty$1, entries, AriaPropNameToAttrNameMap, getPropertyDescriptor, forEach, defineProperties, getPrototypeOf as getPrototypeOf$1, setPrototypeOf, assign, assert, freeze, KEY__SYNTHETIC_MODE, getOwnPropertyDescriptor as getOwnPropertyDescriptor$1, LWC_VERSION_COMMENT_REGEX, LWC_VERSION, getOwnPropertyNames as getOwnPropertyNames$1, getOwnPropertyDescriptors, htmlPropertyToAttribute, ArraySlice, ArrayMap, KEY__SCOPED_CSS, ArraySplice, kebabCaseToCamelCase, StringCharCodeAt, XML_NAMESPACE, XLINK_NAMESPACE, isString, StringSlice, isTrue, SVG_NAMESPACE, KEY__SHADOW_STATIC, KEY__SHADOW_RESOLVER, ArraySome, isNumber, StringReplace, StringTrim, ArraySort, ArrayFrom, htmlEscape, StringCharAt, ArrayUnshift, LOWEST_API_VERSION, KEY__NATIVE_GET_ELEMENT_BY_ID, KEY__NATIVE_QUERY_SELECTOR_ALL, ID_REFERENCING_ATTRIBUTES_SET, KEY__SHADOW_TOKEN, StringSplit, arrayEvery, ArrayIncludes, ArrayCopyWithin, ArrayFill, ArrayReverse, ArrayShift } from '@lwc/shared';
5
5
  export { setFeatureFlag, setFeatureFlagForTest } from '@lwc/features';
6
6
 
7
7
  /*
@@ -176,7 +176,208 @@ function logWarnOnce(message, vm) {
176
176
  }
177
177
 
178
178
  /*
179
- * Copyright (c) 2019, salesforce.com, inc.
179
+ * Copyright (c) 2018, salesforce.com, inc.
180
+ * All rights reserved.
181
+ * SPDX-License-Identifier: MIT
182
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
183
+ */
184
+ let nextTickCallbackQueue = [];
185
+ const SPACE_CHAR = 32;
186
+ const EmptyObject = seal(create(null));
187
+ const EmptyArray = seal([]);
188
+ function flushCallbackQueue() {
189
+ if (process.env.NODE_ENV !== 'production') {
190
+ if (nextTickCallbackQueue.length === 0) {
191
+ throw new Error(`Internal Error: If callbackQueue is scheduled, it is because there must be at least one callback on this pending queue.`);
192
+ }
193
+ }
194
+ const callbacks = nextTickCallbackQueue;
195
+ nextTickCallbackQueue = []; // reset to a new queue
196
+ for (let i = 0, len = callbacks.length; i < len; i += 1) {
197
+ callbacks[i]();
198
+ }
199
+ }
200
+ function addCallbackToNextTick(callback) {
201
+ if (process.env.NODE_ENV !== 'production') {
202
+ if (!isFunction$1(callback)) {
203
+ throw new Error(`Internal Error: addCallbackToNextTick() can only accept a function callback`);
204
+ }
205
+ }
206
+ if (nextTickCallbackQueue.length === 0) {
207
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
208
+ Promise.resolve().then(flushCallbackQueue);
209
+ }
210
+ ArrayPush$1.call(nextTickCallbackQueue, callback);
211
+ }
212
+ function guid() {
213
+ function s4() {
214
+ return Math.floor((1 + Math.random()) * 0x10000)
215
+ .toString(16)
216
+ .substring(1);
217
+ }
218
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
219
+ }
220
+ // Borrowed from Vue template compiler.
221
+ // https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16
222
+ const DECLARATION_DELIMITER = /;(?![^(]*\))/g;
223
+ const PROPERTY_DELIMITER = /:(.+)/;
224
+ function parseStyleText(cssText) {
225
+ const styleMap = {};
226
+ const declarations = cssText.split(DECLARATION_DELIMITER);
227
+ for (const declaration of declarations) {
228
+ if (declaration) {
229
+ const [prop, value] = declaration.split(PROPERTY_DELIMITER);
230
+ if (prop !== undefined && value !== undefined) {
231
+ styleMap[prop.trim()] = value.trim();
232
+ }
233
+ }
234
+ }
235
+ return styleMap;
236
+ }
237
+ // Make a shallow copy of an object but omit the given key
238
+ function cloneAndOmitKey(object, keyToOmit) {
239
+ const result = {};
240
+ for (const key of keys(object)) {
241
+ if (key !== keyToOmit) {
242
+ result[key] = object[key];
243
+ }
244
+ }
245
+ return result;
246
+ }
247
+ function flattenStylesheets(stylesheets) {
248
+ const list = [];
249
+ for (const stylesheet of stylesheets) {
250
+ if (!isArray$1(stylesheet)) {
251
+ list.push(stylesheet);
252
+ }
253
+ else {
254
+ list.push(...flattenStylesheets(stylesheet));
255
+ }
256
+ }
257
+ return list;
258
+ }
259
+ // Throw an error if we're running in prod mode. Ensures code is truly removed from prod mode.
260
+ function assertNotProd() {
261
+ /* istanbul ignore if */
262
+ if (process.env.NODE_ENV === 'production') {
263
+ // this method should never leak to prod
264
+ throw new ReferenceError();
265
+ }
266
+ }
267
+ function shouldBeFormAssociated(Ctor) {
268
+ const ctorFormAssociated = Boolean(Ctor.formAssociated);
269
+ const apiVersion = getComponentAPIVersion(Ctor);
270
+ const apiFeatureEnabled = isAPIFeatureEnabled(7 /* APIFeature.ENABLE_ELEMENT_INTERNALS_AND_FACE */, apiVersion);
271
+ if (process.env.NODE_ENV !== 'production' && ctorFormAssociated && !apiFeatureEnabled) {
272
+ const tagName = getComponentRegisteredName(Ctor);
273
+ logWarnOnce(`Component <${tagName}> set static formAssociated to true, but form ` +
274
+ `association is not enabled because the API version is ${apiVersion}. To enable form association, ` +
275
+ `update the LWC component API version to 61 or above. https://lwc.dev/guide/versioning`);
276
+ }
277
+ return ctorFormAssociated && apiFeatureEnabled;
278
+ }
279
+
280
+ /*
281
+ * Copyright (c) 2024, Salesforce, Inc.
282
+ * All rights reserved.
283
+ * SPDX-License-Identifier: MIT
284
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
285
+ */
286
+ //
287
+ // Do additional mutation tracking for DevTools performance profiling, in dev mode only.
288
+ //
289
+ const reactiveObserversToVMs = new WeakMap();
290
+ const targetsToPropertyKeys = new WeakMap();
291
+ let mutationLogs = [];
292
+ /**
293
+ * Flush all the logs we've written so far and return the current logs.
294
+ */
295
+ function getAndFlushMutationLogs() {
296
+ assertNotProd();
297
+ const result = mutationLogs;
298
+ mutationLogs = [];
299
+ return result;
300
+ }
301
+ /**
302
+ * Log a new mutation for this reactive observer.
303
+ * @param reactiveObserver - relevant ReactiveObserver
304
+ * @param target - target object that is being observed
305
+ * @param key - key (property) that was mutated
306
+ */
307
+ function logMutation(reactiveObserver, target, key) {
308
+ assertNotProd();
309
+ const parentKey = targetsToPropertyKeys.get(target);
310
+ const vm = reactiveObserversToVMs.get(reactiveObserver);
311
+ /* istanbul ignore if */
312
+ if (isUndefined$1(vm)) {
313
+ // VM should only be undefined in Vitest tests, where a reactive observer is not always associated with a VM
314
+ // because the unit tests just create Reactive Observers on-the-fly.
315
+ // Note we could explicitly target Vitest with `process.env.NODE_ENV === 'test'`, but then that would also
316
+ // affect our downstream consumers' Jest/Vitest tests, and we don't want to throw an error just for a logger.
317
+ if (process.env.NODE_ENV === 'test-karma-lwc') {
318
+ throw new Error('The VM should always be defined except possibly in unit tests');
319
+ }
320
+ }
321
+ else {
322
+ const stringKey = toString$1(key);
323
+ let prop;
324
+ if (isUndefined$1(parentKey)) {
325
+ prop = stringKey;
326
+ }
327
+ else if (/^\w+$/.test(stringKey)) {
328
+ // Human-readable prop like `items[0].name` on a deep object/array
329
+ prop = `${toString$1(parentKey)}.${stringKey}`;
330
+ }
331
+ else {
332
+ // e.g. `obj["prop with spaces"]`
333
+ prop = `${toString$1(parentKey)}[${JSON.stringify(stringKey)}]`;
334
+ }
335
+ ArrayPush$1.call(mutationLogs, { vm, prop });
336
+ }
337
+ }
338
+ /**
339
+ * Flush logs associated with a given VM.
340
+ * @param vm - given VM
341
+ */
342
+ function flushMutationLogsForVM(vm) {
343
+ assertNotProd();
344
+ mutationLogs = ArrayFilter.call(mutationLogs, (log) => log.vm !== vm);
345
+ }
346
+ /**
347
+ * Mark this ReactiveObserver as related to this VM. This is only needed for mutation tracking in dev mode.
348
+ * @param reactiveObserver
349
+ * @param vm
350
+ */
351
+ function associateReactiveObserverWithVM(reactiveObserver, vm) {
352
+ assertNotProd();
353
+ reactiveObserversToVMs.set(reactiveObserver, vm);
354
+ }
355
+ /**
356
+ * Deeply track all objects in a target and associate with a given key.
357
+ * @param key - key associated with the object in the component
358
+ * @param target - tracked target object
359
+ */
360
+ function trackTargetForMutationLogging(key, target) {
361
+ assertNotProd();
362
+ if (isObject(target) && !isNull(target)) {
363
+ // only track non-primitives; others are invalid as WeakMap keys
364
+ targetsToPropertyKeys.set(target, key);
365
+ // Deeply traverse arrays and objects to track every object within
366
+ if (isArray$1(target)) {
367
+ for (let i = 0; i < target.length; i++) {
368
+ trackTargetForMutationLogging(`${toString$1(key)}[${i}]`, target[i]);
369
+ }
370
+ }
371
+ else {
372
+ for (const prop of keys(target)) {
373
+ trackTargetForMutationLogging(`${toString$1(key)}.${prop}`, target[prop]);
374
+ }
375
+ }
376
+ }
377
+ }
378
+
379
+ /*
380
+ * Copyright (c) 2024, Salesforce, Inc.
180
381
  * All rights reserved.
181
382
  * SPDX-License-Identifier: MIT
182
383
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -199,6 +400,9 @@ function valueMutated(target, key) {
199
400
  if (!isUndefined$1(reactiveObservers)) {
200
401
  for (let i = 0, len = reactiveObservers.length; i < len; i += 1) {
201
402
  const ro = reactiveObservers[i];
403
+ if (process.env.NODE_ENV !== 'production') {
404
+ logMutation(ro, target, key);
405
+ }
202
406
  ro.notify();
203
407
  }
204
408
  }
@@ -358,7 +562,7 @@ class SignalTracker {
358
562
  }
359
563
 
360
564
  /*
361
- * Copyright (c) 2018, salesforce.com, inc.
565
+ * Copyright (c) 2024, Salesforce, Inc.
362
566
  * All rights reserved.
363
567
  * SPDX-License-Identifier: MIT
364
568
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -403,108 +607,6 @@ function createReactiveObserver(callback) {
403
607
  return process.env.IS_BROWSER ? new ReactiveObserver(callback) : DUMMY_REACTIVE_OBSERVER;
404
608
  }
405
609
 
406
- /*
407
- * Copyright (c) 2018, salesforce.com, inc.
408
- * All rights reserved.
409
- * SPDX-License-Identifier: MIT
410
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
411
- */
412
- let nextTickCallbackQueue = [];
413
- const SPACE_CHAR = 32;
414
- const EmptyObject = seal(create(null));
415
- const EmptyArray = seal([]);
416
- function flushCallbackQueue() {
417
- if (process.env.NODE_ENV !== 'production') {
418
- if (nextTickCallbackQueue.length === 0) {
419
- throw new Error(`Internal Error: If callbackQueue is scheduled, it is because there must be at least one callback on this pending queue.`);
420
- }
421
- }
422
- const callbacks = nextTickCallbackQueue;
423
- nextTickCallbackQueue = []; // reset to a new queue
424
- for (let i = 0, len = callbacks.length; i < len; i += 1) {
425
- callbacks[i]();
426
- }
427
- }
428
- function addCallbackToNextTick(callback) {
429
- if (process.env.NODE_ENV !== 'production') {
430
- if (!isFunction$1(callback)) {
431
- throw new Error(`Internal Error: addCallbackToNextTick() can only accept a function callback`);
432
- }
433
- }
434
- if (nextTickCallbackQueue.length === 0) {
435
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
436
- Promise.resolve().then(flushCallbackQueue);
437
- }
438
- ArrayPush$1.call(nextTickCallbackQueue, callback);
439
- }
440
- function guid() {
441
- function s4() {
442
- return Math.floor((1 + Math.random()) * 0x10000)
443
- .toString(16)
444
- .substring(1);
445
- }
446
- return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
447
- }
448
- // Borrowed from Vue template compiler.
449
- // https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16
450
- const DECLARATION_DELIMITER = /;(?![^(]*\))/g;
451
- const PROPERTY_DELIMITER = /:(.+)/;
452
- function parseStyleText(cssText) {
453
- const styleMap = {};
454
- const declarations = cssText.split(DECLARATION_DELIMITER);
455
- for (const declaration of declarations) {
456
- if (declaration) {
457
- const [prop, value] = declaration.split(PROPERTY_DELIMITER);
458
- if (prop !== undefined && value !== undefined) {
459
- styleMap[prop.trim()] = value.trim();
460
- }
461
- }
462
- }
463
- return styleMap;
464
- }
465
- // Make a shallow copy of an object but omit the given key
466
- function cloneAndOmitKey(object, keyToOmit) {
467
- const result = {};
468
- for (const key of keys(object)) {
469
- if (key !== keyToOmit) {
470
- result[key] = object[key];
471
- }
472
- }
473
- return result;
474
- }
475
- function flattenStylesheets(stylesheets) {
476
- const list = [];
477
- for (const stylesheet of stylesheets) {
478
- if (!isArray$1(stylesheet)) {
479
- list.push(stylesheet);
480
- }
481
- else {
482
- list.push(...flattenStylesheets(stylesheet));
483
- }
484
- }
485
- return list;
486
- }
487
- // Throw an error if we're running in prod mode. Ensures code is truly removed from prod mode.
488
- function assertNotProd() {
489
- /* istanbul ignore if */
490
- if (process.env.NODE_ENV === 'production') {
491
- // this method should never leak to prod
492
- throw new ReferenceError();
493
- }
494
- }
495
- function shouldBeFormAssociated(Ctor) {
496
- const ctorFormAssociated = Boolean(Ctor.formAssociated);
497
- const apiVersion = getComponentAPIVersion(Ctor);
498
- const apiFeatureEnabled = isAPIFeatureEnabled(7 /* APIFeature.ENABLE_ELEMENT_INTERNALS_AND_FACE */, apiVersion);
499
- if (process.env.NODE_ENV !== 'production' && ctorFormAssociated && !apiFeatureEnabled) {
500
- const tagName = getComponentRegisteredName(Ctor);
501
- logWarnOnce(`Component <${tagName}> set static formAssociated to true, but form ` +
502
- `association is not enabled because the API version is ${apiVersion}. To enable form association, ` +
503
- `update the LWC component API version to 61 or above. https://lwc.dev/guide/versioning`);
504
- }
505
- return ctorFormAssociated && apiFeatureEnabled;
506
- }
507
-
508
610
  /*
509
611
  * Copyright (c) 2020, salesforce.com, inc.
510
612
  * All rights reserved.
@@ -2117,6 +2219,9 @@ function createConfigWatcher(component, configCallback, callbackWhenConfigIsRead
2117
2219
  });
2118
2220
  }
2119
2221
  });
2222
+ if (process.env.NODE_ENV !== 'production') {
2223
+ associateReactiveObserverWithVM(ro, getAssociatedVM(component));
2224
+ }
2120
2225
  const computeConfigAndUpdate = () => {
2121
2226
  let config;
2122
2227
  ro.observe(() => (config = configCallback(component)));
@@ -2405,6 +2510,9 @@ function internalTrackDecorator(key) {
2405
2510
  }
2406
2511
  }
2407
2512
  const reactiveOrAnyValue = getReactiveProxy(newValue);
2513
+ if (process.env.NODE_ENV !== 'production') {
2514
+ trackTargetForMutationLogging(key, newValue);
2515
+ }
2408
2516
  updateComponentValue(vm, key, reactiveOrAnyValue);
2409
2517
  },
2410
2518
  enumerable: true,
@@ -5690,7 +5798,7 @@ const api = freeze({
5690
5798
  });
5691
5799
 
5692
5800
  /*
5693
- * Copyright (c) 2018, salesforce.com, inc.
5801
+ * Copyright (c) 2024, Salesforce, Inc.
5694
5802
  * All rights reserved.
5695
5803
  * SPDX-License-Identifier: MIT
5696
5804
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -5706,6 +5814,26 @@ const operationIdNameMapping = [
5706
5814
  'lwc-hydrate',
5707
5815
  'lwc-rehydrate',
5708
5816
  ];
5817
+ const operationTooltipMapping = [
5818
+ // constructor
5819
+ 'component constructor()',
5820
+ // render
5821
+ 'component render() and virtual DOM rendered',
5822
+ // patch
5823
+ 'component DOM rendered',
5824
+ // connectedCallback
5825
+ 'component connectedCallback()',
5826
+ // renderedCallback
5827
+ 'component renderedCallback()',
5828
+ // disconnectedCallback
5829
+ 'component disconnectedCallback()',
5830
+ // errorCallback
5831
+ 'component errorCallback()',
5832
+ // lwc-hydrate
5833
+ 'component first rendered',
5834
+ // lwc-rehydrate
5835
+ 'component re-rendered',
5836
+ ];
5709
5837
  // Even if all the browser the engine supports implements the UserTiming API, we need to guard the measure APIs.
5710
5838
  // JSDom (used in Jest) for example doesn't implement the UserTiming APIs.
5711
5839
  const isUserTimingSupported = typeof performance !== 'undefined' &&
@@ -5720,8 +5848,17 @@ const start = !isUserTimingSupported
5720
5848
  };
5721
5849
  const end = !isUserTimingSupported
5722
5850
  ? noop
5723
- : (measureName, markName) => {
5724
- performance.measure(measureName, markName);
5851
+ : (measureName, markName, devtools) => {
5852
+ performance.measure(measureName, {
5853
+ start: markName,
5854
+ detail: {
5855
+ devtools: {
5856
+ dataType: 'track-entry',
5857
+ track: '⚡️ Lightning Web Components',
5858
+ ...devtools,
5859
+ },
5860
+ },
5861
+ });
5725
5862
  // Clear the created marks and measure to avoid filling the performance entries buffer.
5726
5863
  // Note: Even if the entries get deleted, existing PerformanceObservers preserve a copy of those entries.
5727
5864
  performance.clearMarks(markName);
@@ -5738,6 +5875,66 @@ function getMarkName(opId, vm) {
5738
5875
  // the right measures for components that are recursive.
5739
5876
  return `${getMeasureName(opId, vm)} - ${vm.idx}`;
5740
5877
  }
5878
+ function getProperties(vm) {
5879
+ return [
5880
+ ['Tag Name', vm.tagName],
5881
+ ['Component ID', String(vm.idx)],
5882
+ ['Render Mode', vm.renderMode === 0 /* RenderMode.Light */ ? 'light DOM' : 'shadow DOM'],
5883
+ ['Shadow Mode', vm.shadowMode === 0 /* ShadowMode.Native */ ? 'native' : 'synthetic'],
5884
+ ];
5885
+ }
5886
+ // Create a list of tag names to the properties that were mutated, to help answer the question of
5887
+ // "why did this component re-render?"
5888
+ function getMutationProperties(mutationLogs) {
5889
+ // `mutationLogs` should never have length 0, but bail out if it does for whatever reason
5890
+ if (isUndefined$1(mutationLogs)) {
5891
+ return EmptyArray;
5892
+ }
5893
+ if (!mutationLogs.length) {
5894
+ // Currently this only occurs for experimental signals, because those mutations are not triggered by accessors
5895
+ // TODO [#4546]: support signals in mutation logging
5896
+ return EmptyArray;
5897
+ }
5898
+ // Keep track of unique IDs per tag name so we can just report a raw count at the end, e.g.
5899
+ // `<x-foo> (x2)` to indicate that two instances of `<x-foo>` were rendered.
5900
+ const tagNamesToIdsAndProps = new Map();
5901
+ for (const { vm: { tagName, idx }, prop, } of mutationLogs) {
5902
+ let idsAndProps = tagNamesToIdsAndProps.get(tagName);
5903
+ if (isUndefined$1(idsAndProps)) {
5904
+ idsAndProps = { ids: new Set(), keys: new Set() };
5905
+ tagNamesToIdsAndProps.set(tagName, idsAndProps);
5906
+ }
5907
+ idsAndProps.ids.add(idx);
5908
+ idsAndProps.keys.add(prop);
5909
+ }
5910
+ // Sort by tag name
5911
+ const entries = ArraySort.call([...tagNamesToIdsAndProps], (a, b) => a[0].localeCompare(b[0]));
5912
+ const tagNames = ArrayMap.call(entries, (item) => item[0]);
5913
+ // Show e.g. `<x-foo>` for one instance, or `<x-foo> (x2)` for two instances. (\u00D7 is multiplication symbol)
5914
+ const tagNamesToDisplayTagNames = new Map();
5915
+ for (const tagName of tagNames) {
5916
+ const { ids } = tagNamesToIdsAndProps.get(tagName);
5917
+ const displayTagName = `<${tagName}>${ids.size > 1 ? ` (\u00D7${ids.size})` : ''}`;
5918
+ tagNamesToDisplayTagNames.set(tagName, displayTagName);
5919
+ }
5920
+ // Summary row
5921
+ const usePlural = tagNames.length > 1 || tagNamesToIdsAndProps.get(tagNames[0]).ids.size > 1;
5922
+ const result = [
5923
+ [
5924
+ `Re-rendered Component${usePlural ? 's' : ''}`,
5925
+ ArrayJoin.call(ArrayMap.call(tagNames, (_) => tagNamesToDisplayTagNames.get(_)), ', '),
5926
+ ],
5927
+ ];
5928
+ // Detail rows
5929
+ for (const [prettyTagName, { keys }] of entries) {
5930
+ const displayTagName = tagNamesToDisplayTagNames.get(prettyTagName);
5931
+ ArrayPush$1.call(result, [displayTagName, ArrayJoin.call(ArraySort.call([...keys]), ', ')]);
5932
+ }
5933
+ return result;
5934
+ }
5935
+ function getTooltipText(measureName, opId) {
5936
+ return `${measureName} - ${operationTooltipMapping[opId]}`;
5937
+ }
5741
5938
  /** Indicates if operations should be logged via the User Timing API. */
5742
5939
  const isMeasureEnabled = process.env.NODE_ENV !== 'production';
5743
5940
  /** Indicates if operations should be logged by the profiler. */
@@ -5775,30 +5972,61 @@ function logOperationEnd(opId, vm) {
5775
5972
  if (isMeasureEnabled) {
5776
5973
  const markName = getMarkName(opId, vm);
5777
5974
  const measureName = getMeasureName(opId, vm);
5778
- end(measureName, markName);
5975
+ end(measureName, markName, {
5976
+ color: opId === 1 /* OperationId.Render */ ? 'primary' : 'secondary',
5977
+ tooltipText: getTooltipText(measureName, opId),
5978
+ properties: getProperties(vm),
5979
+ });
5779
5980
  }
5780
5981
  if (isProfilerEnabled) {
5781
5982
  currentDispatcher(opId, 1 /* Phase.Stop */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
5782
5983
  }
5783
5984
  }
5784
- function logGlobalOperationStart(opId, vm) {
5985
+ function logGlobalOperationStart(opId) {
5785
5986
  if (isMeasureEnabled) {
5786
- const opName = getOperationName(opId);
5787
- const markName = isUndefined$1(vm) ? opName : getMarkName(opId, vm);
5987
+ const markName = getOperationName(opId);
5988
+ start(markName);
5989
+ }
5990
+ if (isProfilerEnabled) {
5991
+ currentDispatcher(opId, 0 /* Phase.Start */);
5992
+ }
5993
+ }
5994
+ function logGlobalOperationStartWithVM(opId, vm) {
5995
+ if (isMeasureEnabled) {
5996
+ const markName = getMarkName(opId, vm);
5788
5997
  start(markName);
5789
5998
  }
5790
5999
  if (isProfilerEnabled) {
5791
- currentDispatcher(opId, 0 /* Phase.Start */, vm?.tagName, vm?.idx, vm?.renderMode, vm?.shadowMode);
6000
+ currentDispatcher(opId, 0 /* Phase.Start */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
6001
+ }
6002
+ }
6003
+ function logGlobalOperationEnd(opId, mutationLogs) {
6004
+ if (isMeasureEnabled) {
6005
+ const opName = getOperationName(opId);
6006
+ const markName = opName;
6007
+ end(opName, markName, {
6008
+ // not really an error, but we want to draw attention to re-renders since folks may want to debug it
6009
+ color: 'error',
6010
+ tooltipText: getTooltipText(opName, opId),
6011
+ properties: getMutationProperties(mutationLogs),
6012
+ });
6013
+ }
6014
+ if (isProfilerEnabled) {
6015
+ currentDispatcher(opId, 1 /* Phase.Stop */);
5792
6016
  }
5793
6017
  }
5794
- function logGlobalOperationEnd(opId, vm) {
6018
+ function logGlobalOperationEndWithVM(opId, vm) {
5795
6019
  if (isMeasureEnabled) {
5796
6020
  const opName = getOperationName(opId);
5797
- const markName = isUndefined$1(vm) ? opName : getMarkName(opId, vm);
5798
- end(opName, markName);
6021
+ const markName = getMarkName(opId, vm);
6022
+ end(opName, markName, {
6023
+ color: 'tertiary',
6024
+ tooltipText: getTooltipText(opName, opId),
6025
+ properties: getProperties(vm),
6026
+ });
5799
6027
  }
5800
6028
  if (isProfilerEnabled) {
5801
- currentDispatcher(opId, 1 /* Phase.Stop */, vm?.tagName, vm?.idx, vm?.renderMode, vm?.shadowMode);
6029
+ currentDispatcher(opId, 1 /* Phase.Stop */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
5802
6030
  }
5803
6031
  }
5804
6032
 
@@ -5854,6 +6082,12 @@ function getVMBeingRendered() {
5854
6082
  function setVMBeingRendered(vm) {
5855
6083
  vmBeingRendered = vm;
5856
6084
  }
6085
+ const VALID_SCOPE_TOKEN_REGEX = /^[a-zA-Z0-9\-_.]+$/;
6086
+ // See W-16614556
6087
+ // TODO [#2826]: freeze the template object
6088
+ function isValidScopeToken(token) {
6089
+ return isString(token) && VALID_SCOPE_TOKEN_REGEX.test(token);
6090
+ }
5857
6091
  function validateSlots(vm) {
5858
6092
  assertNotProd(); // this method should never leak to prod
5859
6093
  const { cmpSlots } = vm;
@@ -6006,9 +6240,9 @@ function buildParseFragmentFn(createFragmentFn) {
6006
6240
  }
6007
6241
  }
6008
6242
  // See W-16614556
6009
- if ((hasStyleToken && !isString(stylesheetToken)) ||
6010
- (hasLegacyToken && !isString(legacyStylesheetToken))) {
6011
- throw new Error('stylesheet token must be a string');
6243
+ if ((hasStyleToken && !isValidScopeToken(stylesheetToken)) ||
6244
+ (hasLegacyToken && !isValidScopeToken(legacyStylesheetToken))) {
6245
+ throw new Error('stylesheet token must be a valid string');
6012
6246
  }
6013
6247
  // If legacy stylesheet tokens are required, then add them to the rendered string
6014
6248
  const stylesheetTokenToRender = stylesheetToken + (hasLegacyToken ? ` ${legacyStylesheetToken}` : '');
@@ -6300,13 +6534,17 @@ function getComponentAPIVersion(Ctor) {
6300
6534
  return apiVersion;
6301
6535
  }
6302
6536
  function getTemplateReactiveObserver(vm) {
6303
- return createReactiveObserver(() => {
6537
+ const reactiveObserver = createReactiveObserver(() => {
6304
6538
  const { isDirty } = vm;
6305
6539
  if (isFalse(isDirty)) {
6306
6540
  markComponentAsDirty(vm);
6307
6541
  scheduleRehydration(vm);
6308
6542
  }
6309
6543
  });
6544
+ if (process.env.NODE_ENV !== 'production') {
6545
+ associateReactiveObserverWithVM(reactiveObserver, vm);
6546
+ }
6547
+ return reactiveObserver;
6310
6548
  }
6311
6549
  function resetTemplateObserverAndUnsubscribe(vm) {
6312
6550
  const { tro, component } = vm;
@@ -6381,7 +6619,12 @@ function rerenderVM(vm) {
6381
6619
  }
6382
6620
  function connectRootElement(elm) {
6383
6621
  const vm = getAssociatedVM(elm);
6384
- logGlobalOperationStart(7 /* OperationId.GlobalHydrate */, vm);
6622
+ if (process.env.NODE_ENV !== 'production') {
6623
+ // Flush any logs for this VM so that the initial properties from the constructor don't "count"
6624
+ // in subsequent re-renders (lwc-rehydrate). Right now we're at the first render (lwc-hydrate).
6625
+ flushMutationLogsForVM(vm);
6626
+ }
6627
+ logGlobalOperationStartWithVM(7 /* OperationId.GlobalHydrate */, vm);
6385
6628
  // Usually means moving the element from one place to another, which is observable via
6386
6629
  // life-cycle hooks.
6387
6630
  if (vm.state === 1 /* VMState.connected */) {
@@ -6389,7 +6632,7 @@ function connectRootElement(elm) {
6389
6632
  }
6390
6633
  runConnectedCallback(vm);
6391
6634
  rehydrate(vm);
6392
- logGlobalOperationEnd(7 /* OperationId.GlobalHydrate */, vm);
6635
+ logGlobalOperationEndWithVM(7 /* OperationId.GlobalHydrate */, vm);
6393
6636
  }
6394
6637
  function disconnectRootElement(elm) {
6395
6638
  const vm = getAssociatedVM(elm);
@@ -6695,6 +6938,9 @@ function runRenderedCallback(vm) {
6695
6938
  }
6696
6939
  let rehydrateQueue = [];
6697
6940
  function flushRehydrationQueue() {
6941
+ // Gather the logs before rehydration starts so they can be reported at the end of rehydration.
6942
+ // Note that we also clear all existing logs at this point so that subsequent re-renders start from a clean slate.
6943
+ const mutationLogs = process.env.NODE_ENV !== 'production' ? getAndFlushMutationLogs() : undefined;
6698
6944
  logGlobalOperationStart(8 /* OperationId.GlobalRehydrate */);
6699
6945
  if (process.env.NODE_ENV !== 'production') {
6700
6946
  assert.invariant(rehydrateQueue.length, `If rehydrateQueue was scheduled, it is because there must be at least one VM on this pending queue instead of ${rehydrateQueue}.`);
@@ -6715,13 +6961,13 @@ function flushRehydrationQueue() {
6715
6961
  ArrayUnshift.apply(rehydrateQueue, ArraySlice.call(vms, i + 1));
6716
6962
  }
6717
6963
  // we need to end the measure before throwing.
6718
- logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */);
6964
+ logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */, mutationLogs);
6719
6965
  // re-throwing the original error will break the current tick, but since the next tick is
6720
6966
  // already scheduled, it should continue patching the rest.
6721
6967
  throw error;
6722
6968
  }
6723
6969
  }
6724
- logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */);
6970
+ logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */, mutationLogs);
6725
6971
  }
6726
6972
  function runConnectedCallback(vm) {
6727
6973
  const { state } = vm;
@@ -8089,5 +8335,5 @@ function readonly(obj) {
8089
8335
  }
8090
8336
 
8091
8337
  export { LightningElement, profilerControl as __unstable__ProfilerControl, reportingControl as __unstable__ReportingControl, api$1 as api, computeShadowAndRenderMode, connectRootElement, createContextProviderWithRegister, createVM, disconnectRootElement, freezeTemplate, getAssociatedVMIfPresent, getComponentAPIVersion, getComponentConstructor, getComponentDef, getComponentHtmlPrototype, hydrateRoot, isComponentConstructor, parseFragment, parseSVGFragment, readonly, registerComponent, registerDecorators, registerTemplate, runFormAssociatedCallback, runFormDisabledCallback, runFormResetCallback, runFormStateRestoreCallback, sanitizeAttribute, setHooks, shouldBeFormAssociated, swapComponent, swapStyle, swapTemplate, track, unwrap, wire };
8092
- /** version: 8.0.0-alpha.1 */
8338
+ /** version: 8.0.0 */
8093
8339
  //# sourceMappingURL=index.js.map