@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.cjs.js CHANGED
@@ -180,7 +180,208 @@ function logWarnOnce(message, vm) {
180
180
  }
181
181
 
182
182
  /*
183
- * Copyright (c) 2019, salesforce.com, inc.
183
+ * Copyright (c) 2018, salesforce.com, inc.
184
+ * All rights reserved.
185
+ * SPDX-License-Identifier: MIT
186
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
187
+ */
188
+ let nextTickCallbackQueue = [];
189
+ const SPACE_CHAR = 32;
190
+ const EmptyObject = shared.seal(shared.create(null));
191
+ const EmptyArray = shared.seal([]);
192
+ function flushCallbackQueue() {
193
+ if (process.env.NODE_ENV !== 'production') {
194
+ if (nextTickCallbackQueue.length === 0) {
195
+ throw new Error(`Internal Error: If callbackQueue is scheduled, it is because there must be at least one callback on this pending queue.`);
196
+ }
197
+ }
198
+ const callbacks = nextTickCallbackQueue;
199
+ nextTickCallbackQueue = []; // reset to a new queue
200
+ for (let i = 0, len = callbacks.length; i < len; i += 1) {
201
+ callbacks[i]();
202
+ }
203
+ }
204
+ function addCallbackToNextTick(callback) {
205
+ if (process.env.NODE_ENV !== 'production') {
206
+ if (!shared.isFunction(callback)) {
207
+ throw new Error(`Internal Error: addCallbackToNextTick() can only accept a function callback`);
208
+ }
209
+ }
210
+ if (nextTickCallbackQueue.length === 0) {
211
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
212
+ Promise.resolve().then(flushCallbackQueue);
213
+ }
214
+ shared.ArrayPush.call(nextTickCallbackQueue, callback);
215
+ }
216
+ function guid() {
217
+ function s4() {
218
+ return Math.floor((1 + Math.random()) * 0x10000)
219
+ .toString(16)
220
+ .substring(1);
221
+ }
222
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
223
+ }
224
+ // Borrowed from Vue template compiler.
225
+ // https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16
226
+ const DECLARATION_DELIMITER = /;(?![^(]*\))/g;
227
+ const PROPERTY_DELIMITER = /:(.+)/;
228
+ function parseStyleText(cssText) {
229
+ const styleMap = {};
230
+ const declarations = cssText.split(DECLARATION_DELIMITER);
231
+ for (const declaration of declarations) {
232
+ if (declaration) {
233
+ const [prop, value] = declaration.split(PROPERTY_DELIMITER);
234
+ if (prop !== undefined && value !== undefined) {
235
+ styleMap[prop.trim()] = value.trim();
236
+ }
237
+ }
238
+ }
239
+ return styleMap;
240
+ }
241
+ // Make a shallow copy of an object but omit the given key
242
+ function cloneAndOmitKey(object, keyToOmit) {
243
+ const result = {};
244
+ for (const key of shared.keys(object)) {
245
+ if (key !== keyToOmit) {
246
+ result[key] = object[key];
247
+ }
248
+ }
249
+ return result;
250
+ }
251
+ function flattenStylesheets(stylesheets) {
252
+ const list = [];
253
+ for (const stylesheet of stylesheets) {
254
+ if (!shared.isArray(stylesheet)) {
255
+ list.push(stylesheet);
256
+ }
257
+ else {
258
+ list.push(...flattenStylesheets(stylesheet));
259
+ }
260
+ }
261
+ return list;
262
+ }
263
+ // Throw an error if we're running in prod mode. Ensures code is truly removed from prod mode.
264
+ function assertNotProd() {
265
+ /* istanbul ignore if */
266
+ if (process.env.NODE_ENV === 'production') {
267
+ // this method should never leak to prod
268
+ throw new ReferenceError();
269
+ }
270
+ }
271
+ function shouldBeFormAssociated(Ctor) {
272
+ const ctorFormAssociated = Boolean(Ctor.formAssociated);
273
+ const apiVersion = getComponentAPIVersion(Ctor);
274
+ const apiFeatureEnabled = shared.isAPIFeatureEnabled(7 /* APIFeature.ENABLE_ELEMENT_INTERNALS_AND_FACE */, apiVersion);
275
+ if (process.env.NODE_ENV !== 'production' && ctorFormAssociated && !apiFeatureEnabled) {
276
+ const tagName = getComponentRegisteredName(Ctor);
277
+ logWarnOnce(`Component <${tagName}> set static formAssociated to true, but form ` +
278
+ `association is not enabled because the API version is ${apiVersion}. To enable form association, ` +
279
+ `update the LWC component API version to 61 or above. https://lwc.dev/guide/versioning`);
280
+ }
281
+ return ctorFormAssociated && apiFeatureEnabled;
282
+ }
283
+
284
+ /*
285
+ * Copyright (c) 2024, Salesforce, Inc.
286
+ * All rights reserved.
287
+ * SPDX-License-Identifier: MIT
288
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
289
+ */
290
+ //
291
+ // Do additional mutation tracking for DevTools performance profiling, in dev mode only.
292
+ //
293
+ const reactiveObserversToVMs = new WeakMap();
294
+ const targetsToPropertyKeys = new WeakMap();
295
+ let mutationLogs = [];
296
+ /**
297
+ * Flush all the logs we've written so far and return the current logs.
298
+ */
299
+ function getAndFlushMutationLogs() {
300
+ assertNotProd();
301
+ const result = mutationLogs;
302
+ mutationLogs = [];
303
+ return result;
304
+ }
305
+ /**
306
+ * Log a new mutation for this reactive observer.
307
+ * @param reactiveObserver - relevant ReactiveObserver
308
+ * @param target - target object that is being observed
309
+ * @param key - key (property) that was mutated
310
+ */
311
+ function logMutation(reactiveObserver, target, key) {
312
+ assertNotProd();
313
+ const parentKey = targetsToPropertyKeys.get(target);
314
+ const vm = reactiveObserversToVMs.get(reactiveObserver);
315
+ /* istanbul ignore if */
316
+ if (shared.isUndefined(vm)) {
317
+ // VM should only be undefined in Vitest tests, where a reactive observer is not always associated with a VM
318
+ // because the unit tests just create Reactive Observers on-the-fly.
319
+ // Note we could explicitly target Vitest with `process.env.NODE_ENV === 'test'`, but then that would also
320
+ // affect our downstream consumers' Jest/Vitest tests, and we don't want to throw an error just for a logger.
321
+ if (process.env.NODE_ENV === 'test-karma-lwc') {
322
+ throw new Error('The VM should always be defined except possibly in unit tests');
323
+ }
324
+ }
325
+ else {
326
+ const stringKey = shared.toString(key);
327
+ let prop;
328
+ if (shared.isUndefined(parentKey)) {
329
+ prop = stringKey;
330
+ }
331
+ else if (/^\w+$/.test(stringKey)) {
332
+ // Human-readable prop like `items[0].name` on a deep object/array
333
+ prop = `${shared.toString(parentKey)}.${stringKey}`;
334
+ }
335
+ else {
336
+ // e.g. `obj["prop with spaces"]`
337
+ prop = `${shared.toString(parentKey)}[${JSON.stringify(stringKey)}]`;
338
+ }
339
+ shared.ArrayPush.call(mutationLogs, { vm, prop });
340
+ }
341
+ }
342
+ /**
343
+ * Flush logs associated with a given VM.
344
+ * @param vm - given VM
345
+ */
346
+ function flushMutationLogsForVM(vm) {
347
+ assertNotProd();
348
+ mutationLogs = shared.ArrayFilter.call(mutationLogs, (log) => log.vm !== vm);
349
+ }
350
+ /**
351
+ * Mark this ReactiveObserver as related to this VM. This is only needed for mutation tracking in dev mode.
352
+ * @param reactiveObserver
353
+ * @param vm
354
+ */
355
+ function associateReactiveObserverWithVM(reactiveObserver, vm) {
356
+ assertNotProd();
357
+ reactiveObserversToVMs.set(reactiveObserver, vm);
358
+ }
359
+ /**
360
+ * Deeply track all objects in a target and associate with a given key.
361
+ * @param key - key associated with the object in the component
362
+ * @param target - tracked target object
363
+ */
364
+ function trackTargetForMutationLogging(key, target) {
365
+ assertNotProd();
366
+ if (shared.isObject(target) && !shared.isNull(target)) {
367
+ // only track non-primitives; others are invalid as WeakMap keys
368
+ targetsToPropertyKeys.set(target, key);
369
+ // Deeply traverse arrays and objects to track every object within
370
+ if (shared.isArray(target)) {
371
+ for (let i = 0; i < target.length; i++) {
372
+ trackTargetForMutationLogging(`${shared.toString(key)}[${i}]`, target[i]);
373
+ }
374
+ }
375
+ else {
376
+ for (const prop of shared.keys(target)) {
377
+ trackTargetForMutationLogging(`${shared.toString(key)}.${prop}`, target[prop]);
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ /*
384
+ * Copyright (c) 2024, Salesforce, Inc.
184
385
  * All rights reserved.
185
386
  * SPDX-License-Identifier: MIT
186
387
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -203,6 +404,9 @@ function valueMutated(target, key) {
203
404
  if (!shared.isUndefined(reactiveObservers)) {
204
405
  for (let i = 0, len = reactiveObservers.length; i < len; i += 1) {
205
406
  const ro = reactiveObservers[i];
407
+ if (process.env.NODE_ENV !== 'production') {
408
+ logMutation(ro, target, key);
409
+ }
206
410
  ro.notify();
207
411
  }
208
412
  }
@@ -362,7 +566,7 @@ class SignalTracker {
362
566
  }
363
567
 
364
568
  /*
365
- * Copyright (c) 2018, salesforce.com, inc.
569
+ * Copyright (c) 2024, Salesforce, Inc.
366
570
  * All rights reserved.
367
571
  * SPDX-License-Identifier: MIT
368
572
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -407,108 +611,6 @@ function createReactiveObserver(callback) {
407
611
  return process.env.IS_BROWSER ? new ReactiveObserver(callback) : DUMMY_REACTIVE_OBSERVER;
408
612
  }
409
613
 
410
- /*
411
- * Copyright (c) 2018, salesforce.com, inc.
412
- * All rights reserved.
413
- * SPDX-License-Identifier: MIT
414
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
415
- */
416
- let nextTickCallbackQueue = [];
417
- const SPACE_CHAR = 32;
418
- const EmptyObject = shared.seal(shared.create(null));
419
- const EmptyArray = shared.seal([]);
420
- function flushCallbackQueue() {
421
- if (process.env.NODE_ENV !== 'production') {
422
- if (nextTickCallbackQueue.length === 0) {
423
- throw new Error(`Internal Error: If callbackQueue is scheduled, it is because there must be at least one callback on this pending queue.`);
424
- }
425
- }
426
- const callbacks = nextTickCallbackQueue;
427
- nextTickCallbackQueue = []; // reset to a new queue
428
- for (let i = 0, len = callbacks.length; i < len; i += 1) {
429
- callbacks[i]();
430
- }
431
- }
432
- function addCallbackToNextTick(callback) {
433
- if (process.env.NODE_ENV !== 'production') {
434
- if (!shared.isFunction(callback)) {
435
- throw new Error(`Internal Error: addCallbackToNextTick() can only accept a function callback`);
436
- }
437
- }
438
- if (nextTickCallbackQueue.length === 0) {
439
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
440
- Promise.resolve().then(flushCallbackQueue);
441
- }
442
- shared.ArrayPush.call(nextTickCallbackQueue, callback);
443
- }
444
- function guid() {
445
- function s4() {
446
- return Math.floor((1 + Math.random()) * 0x10000)
447
- .toString(16)
448
- .substring(1);
449
- }
450
- return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
451
- }
452
- // Borrowed from Vue template compiler.
453
- // https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16
454
- const DECLARATION_DELIMITER = /;(?![^(]*\))/g;
455
- const PROPERTY_DELIMITER = /:(.+)/;
456
- function parseStyleText(cssText) {
457
- const styleMap = {};
458
- const declarations = cssText.split(DECLARATION_DELIMITER);
459
- for (const declaration of declarations) {
460
- if (declaration) {
461
- const [prop, value] = declaration.split(PROPERTY_DELIMITER);
462
- if (prop !== undefined && value !== undefined) {
463
- styleMap[prop.trim()] = value.trim();
464
- }
465
- }
466
- }
467
- return styleMap;
468
- }
469
- // Make a shallow copy of an object but omit the given key
470
- function cloneAndOmitKey(object, keyToOmit) {
471
- const result = {};
472
- for (const key of shared.keys(object)) {
473
- if (key !== keyToOmit) {
474
- result[key] = object[key];
475
- }
476
- }
477
- return result;
478
- }
479
- function flattenStylesheets(stylesheets) {
480
- const list = [];
481
- for (const stylesheet of stylesheets) {
482
- if (!shared.isArray(stylesheet)) {
483
- list.push(stylesheet);
484
- }
485
- else {
486
- list.push(...flattenStylesheets(stylesheet));
487
- }
488
- }
489
- return list;
490
- }
491
- // Throw an error if we're running in prod mode. Ensures code is truly removed from prod mode.
492
- function assertNotProd() {
493
- /* istanbul ignore if */
494
- if (process.env.NODE_ENV === 'production') {
495
- // this method should never leak to prod
496
- throw new ReferenceError();
497
- }
498
- }
499
- function shouldBeFormAssociated(Ctor) {
500
- const ctorFormAssociated = Boolean(Ctor.formAssociated);
501
- const apiVersion = getComponentAPIVersion(Ctor);
502
- const apiFeatureEnabled = shared.isAPIFeatureEnabled(7 /* APIFeature.ENABLE_ELEMENT_INTERNALS_AND_FACE */, apiVersion);
503
- if (process.env.NODE_ENV !== 'production' && ctorFormAssociated && !apiFeatureEnabled) {
504
- const tagName = getComponentRegisteredName(Ctor);
505
- logWarnOnce(`Component <${tagName}> set static formAssociated to true, but form ` +
506
- `association is not enabled because the API version is ${apiVersion}. To enable form association, ` +
507
- `update the LWC component API version to 61 or above. https://lwc.dev/guide/versioning`);
508
- }
509
- return ctorFormAssociated && apiFeatureEnabled;
510
- }
511
-
512
614
  /*
513
615
  * Copyright (c) 2020, salesforce.com, inc.
514
616
  * All rights reserved.
@@ -2121,6 +2223,9 @@ function createConfigWatcher(component, configCallback, callbackWhenConfigIsRead
2121
2223
  });
2122
2224
  }
2123
2225
  });
2226
+ if (process.env.NODE_ENV !== 'production') {
2227
+ associateReactiveObserverWithVM(ro, getAssociatedVM(component));
2228
+ }
2124
2229
  const computeConfigAndUpdate = () => {
2125
2230
  let config;
2126
2231
  ro.observe(() => (config = configCallback(component)));
@@ -2409,6 +2514,9 @@ function internalTrackDecorator(key) {
2409
2514
  }
2410
2515
  }
2411
2516
  const reactiveOrAnyValue = getReactiveProxy(newValue);
2517
+ if (process.env.NODE_ENV !== 'production') {
2518
+ trackTargetForMutationLogging(key, newValue);
2519
+ }
2412
2520
  updateComponentValue(vm, key, reactiveOrAnyValue);
2413
2521
  },
2414
2522
  enumerable: true,
@@ -5694,7 +5802,7 @@ const api = shared.freeze({
5694
5802
  });
5695
5803
 
5696
5804
  /*
5697
- * Copyright (c) 2018, salesforce.com, inc.
5805
+ * Copyright (c) 2024, Salesforce, Inc.
5698
5806
  * All rights reserved.
5699
5807
  * SPDX-License-Identifier: MIT
5700
5808
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -5710,6 +5818,26 @@ const operationIdNameMapping = [
5710
5818
  'lwc-hydrate',
5711
5819
  'lwc-rehydrate',
5712
5820
  ];
5821
+ const operationTooltipMapping = [
5822
+ // constructor
5823
+ 'component constructor()',
5824
+ // render
5825
+ 'component render() and virtual DOM rendered',
5826
+ // patch
5827
+ 'component DOM rendered',
5828
+ // connectedCallback
5829
+ 'component connectedCallback()',
5830
+ // renderedCallback
5831
+ 'component renderedCallback()',
5832
+ // disconnectedCallback
5833
+ 'component disconnectedCallback()',
5834
+ // errorCallback
5835
+ 'component errorCallback()',
5836
+ // lwc-hydrate
5837
+ 'component first rendered',
5838
+ // lwc-rehydrate
5839
+ 'component re-rendered',
5840
+ ];
5713
5841
  // Even if all the browser the engine supports implements the UserTiming API, we need to guard the measure APIs.
5714
5842
  // JSDom (used in Jest) for example doesn't implement the UserTiming APIs.
5715
5843
  const isUserTimingSupported = typeof performance !== 'undefined' &&
@@ -5724,8 +5852,17 @@ const start = !isUserTimingSupported
5724
5852
  };
5725
5853
  const end = !isUserTimingSupported
5726
5854
  ? shared.noop
5727
- : (measureName, markName) => {
5728
- performance.measure(measureName, markName);
5855
+ : (measureName, markName, devtools) => {
5856
+ performance.measure(measureName, {
5857
+ start: markName,
5858
+ detail: {
5859
+ devtools: {
5860
+ dataType: 'track-entry',
5861
+ track: '⚡️ Lightning Web Components',
5862
+ ...devtools,
5863
+ },
5864
+ },
5865
+ });
5729
5866
  // Clear the created marks and measure to avoid filling the performance entries buffer.
5730
5867
  // Note: Even if the entries get deleted, existing PerformanceObservers preserve a copy of those entries.
5731
5868
  performance.clearMarks(markName);
@@ -5742,6 +5879,66 @@ function getMarkName(opId, vm) {
5742
5879
  // the right measures for components that are recursive.
5743
5880
  return `${getMeasureName(opId, vm)} - ${vm.idx}`;
5744
5881
  }
5882
+ function getProperties(vm) {
5883
+ return [
5884
+ ['Tag Name', vm.tagName],
5885
+ ['Component ID', String(vm.idx)],
5886
+ ['Render Mode', vm.renderMode === 0 /* RenderMode.Light */ ? 'light DOM' : 'shadow DOM'],
5887
+ ['Shadow Mode', vm.shadowMode === 0 /* ShadowMode.Native */ ? 'native' : 'synthetic'],
5888
+ ];
5889
+ }
5890
+ // Create a list of tag names to the properties that were mutated, to help answer the question of
5891
+ // "why did this component re-render?"
5892
+ function getMutationProperties(mutationLogs) {
5893
+ // `mutationLogs` should never have length 0, but bail out if it does for whatever reason
5894
+ if (shared.isUndefined(mutationLogs)) {
5895
+ return EmptyArray;
5896
+ }
5897
+ if (!mutationLogs.length) {
5898
+ // Currently this only occurs for experimental signals, because those mutations are not triggered by accessors
5899
+ // TODO [#4546]: support signals in mutation logging
5900
+ return EmptyArray;
5901
+ }
5902
+ // Keep track of unique IDs per tag name so we can just report a raw count at the end, e.g.
5903
+ // `<x-foo> (x2)` to indicate that two instances of `<x-foo>` were rendered.
5904
+ const tagNamesToIdsAndProps = new Map();
5905
+ for (const { vm: { tagName, idx }, prop, } of mutationLogs) {
5906
+ let idsAndProps = tagNamesToIdsAndProps.get(tagName);
5907
+ if (shared.isUndefined(idsAndProps)) {
5908
+ idsAndProps = { ids: new Set(), keys: new Set() };
5909
+ tagNamesToIdsAndProps.set(tagName, idsAndProps);
5910
+ }
5911
+ idsAndProps.ids.add(idx);
5912
+ idsAndProps.keys.add(prop);
5913
+ }
5914
+ // Sort by tag name
5915
+ const entries = shared.ArraySort.call([...tagNamesToIdsAndProps], (a, b) => a[0].localeCompare(b[0]));
5916
+ const tagNames = shared.ArrayMap.call(entries, (item) => item[0]);
5917
+ // Show e.g. `<x-foo>` for one instance, or `<x-foo> (x2)` for two instances. (\u00D7 is multiplication symbol)
5918
+ const tagNamesToDisplayTagNames = new Map();
5919
+ for (const tagName of tagNames) {
5920
+ const { ids } = tagNamesToIdsAndProps.get(tagName);
5921
+ const displayTagName = `<${tagName}>${ids.size > 1 ? ` (\u00D7${ids.size})` : ''}`;
5922
+ tagNamesToDisplayTagNames.set(tagName, displayTagName);
5923
+ }
5924
+ // Summary row
5925
+ const usePlural = tagNames.length > 1 || tagNamesToIdsAndProps.get(tagNames[0]).ids.size > 1;
5926
+ const result = [
5927
+ [
5928
+ `Re-rendered Component${usePlural ? 's' : ''}`,
5929
+ shared.ArrayJoin.call(shared.ArrayMap.call(tagNames, (_) => tagNamesToDisplayTagNames.get(_)), ', '),
5930
+ ],
5931
+ ];
5932
+ // Detail rows
5933
+ for (const [prettyTagName, { keys }] of entries) {
5934
+ const displayTagName = tagNamesToDisplayTagNames.get(prettyTagName);
5935
+ shared.ArrayPush.call(result, [displayTagName, shared.ArrayJoin.call(shared.ArraySort.call([...keys]), ', ')]);
5936
+ }
5937
+ return result;
5938
+ }
5939
+ function getTooltipText(measureName, opId) {
5940
+ return `${measureName} - ${operationTooltipMapping[opId]}`;
5941
+ }
5745
5942
  /** Indicates if operations should be logged via the User Timing API. */
5746
5943
  const isMeasureEnabled = process.env.NODE_ENV !== 'production';
5747
5944
  /** Indicates if operations should be logged by the profiler. */
@@ -5779,30 +5976,61 @@ function logOperationEnd(opId, vm) {
5779
5976
  if (isMeasureEnabled) {
5780
5977
  const markName = getMarkName(opId, vm);
5781
5978
  const measureName = getMeasureName(opId, vm);
5782
- end(measureName, markName);
5979
+ end(measureName, markName, {
5980
+ color: opId === 1 /* OperationId.Render */ ? 'primary' : 'secondary',
5981
+ tooltipText: getTooltipText(measureName, opId),
5982
+ properties: getProperties(vm),
5983
+ });
5783
5984
  }
5784
5985
  if (isProfilerEnabled) {
5785
5986
  currentDispatcher(opId, 1 /* Phase.Stop */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
5786
5987
  }
5787
5988
  }
5788
- function logGlobalOperationStart(opId, vm) {
5989
+ function logGlobalOperationStart(opId) {
5789
5990
  if (isMeasureEnabled) {
5790
- const opName = getOperationName(opId);
5791
- const markName = shared.isUndefined(vm) ? opName : getMarkName(opId, vm);
5991
+ const markName = getOperationName(opId);
5992
+ start(markName);
5993
+ }
5994
+ if (isProfilerEnabled) {
5995
+ currentDispatcher(opId, 0 /* Phase.Start */);
5996
+ }
5997
+ }
5998
+ function logGlobalOperationStartWithVM(opId, vm) {
5999
+ if (isMeasureEnabled) {
6000
+ const markName = getMarkName(opId, vm);
5792
6001
  start(markName);
5793
6002
  }
5794
6003
  if (isProfilerEnabled) {
5795
- currentDispatcher(opId, 0 /* Phase.Start */, vm?.tagName, vm?.idx, vm?.renderMode, vm?.shadowMode);
6004
+ currentDispatcher(opId, 0 /* Phase.Start */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
6005
+ }
6006
+ }
6007
+ function logGlobalOperationEnd(opId, mutationLogs) {
6008
+ if (isMeasureEnabled) {
6009
+ const opName = getOperationName(opId);
6010
+ const markName = opName;
6011
+ end(opName, markName, {
6012
+ // not really an error, but we want to draw attention to re-renders since folks may want to debug it
6013
+ color: 'error',
6014
+ tooltipText: getTooltipText(opName, opId),
6015
+ properties: getMutationProperties(mutationLogs),
6016
+ });
6017
+ }
6018
+ if (isProfilerEnabled) {
6019
+ currentDispatcher(opId, 1 /* Phase.Stop */);
5796
6020
  }
5797
6021
  }
5798
- function logGlobalOperationEnd(opId, vm) {
6022
+ function logGlobalOperationEndWithVM(opId, vm) {
5799
6023
  if (isMeasureEnabled) {
5800
6024
  const opName = getOperationName(opId);
5801
- const markName = shared.isUndefined(vm) ? opName : getMarkName(opId, vm);
5802
- end(opName, markName);
6025
+ const markName = getMarkName(opId, vm);
6026
+ end(opName, markName, {
6027
+ color: 'tertiary',
6028
+ tooltipText: getTooltipText(opName, opId),
6029
+ properties: getProperties(vm),
6030
+ });
5803
6031
  }
5804
6032
  if (isProfilerEnabled) {
5805
- currentDispatcher(opId, 1 /* Phase.Stop */, vm?.tagName, vm?.idx, vm?.renderMode, vm?.shadowMode);
6033
+ currentDispatcher(opId, 1 /* Phase.Stop */, vm.tagName, vm.idx, vm.renderMode, vm.shadowMode);
5806
6034
  }
5807
6035
  }
5808
6036
 
@@ -5858,6 +6086,12 @@ function getVMBeingRendered() {
5858
6086
  function setVMBeingRendered(vm) {
5859
6087
  vmBeingRendered = vm;
5860
6088
  }
6089
+ const VALID_SCOPE_TOKEN_REGEX = /^[a-zA-Z0-9\-_.]+$/;
6090
+ // See W-16614556
6091
+ // TODO [#2826]: freeze the template object
6092
+ function isValidScopeToken(token) {
6093
+ return shared.isString(token) && VALID_SCOPE_TOKEN_REGEX.test(token);
6094
+ }
5861
6095
  function validateSlots(vm) {
5862
6096
  assertNotProd(); // this method should never leak to prod
5863
6097
  const { cmpSlots } = vm;
@@ -6010,9 +6244,9 @@ function buildParseFragmentFn(createFragmentFn) {
6010
6244
  }
6011
6245
  }
6012
6246
  // See W-16614556
6013
- if ((hasStyleToken && !shared.isString(stylesheetToken)) ||
6014
- (hasLegacyToken && !shared.isString(legacyStylesheetToken))) {
6015
- throw new Error('stylesheet token must be a string');
6247
+ if ((hasStyleToken && !isValidScopeToken(stylesheetToken)) ||
6248
+ (hasLegacyToken && !isValidScopeToken(legacyStylesheetToken))) {
6249
+ throw new Error('stylesheet token must be a valid string');
6016
6250
  }
6017
6251
  // If legacy stylesheet tokens are required, then add them to the rendered string
6018
6252
  const stylesheetTokenToRender = stylesheetToken + (hasLegacyToken ? ` ${legacyStylesheetToken}` : '');
@@ -6304,13 +6538,17 @@ function getComponentAPIVersion(Ctor) {
6304
6538
  return apiVersion;
6305
6539
  }
6306
6540
  function getTemplateReactiveObserver(vm) {
6307
- return createReactiveObserver(() => {
6541
+ const reactiveObserver = createReactiveObserver(() => {
6308
6542
  const { isDirty } = vm;
6309
6543
  if (shared.isFalse(isDirty)) {
6310
6544
  markComponentAsDirty(vm);
6311
6545
  scheduleRehydration(vm);
6312
6546
  }
6313
6547
  });
6548
+ if (process.env.NODE_ENV !== 'production') {
6549
+ associateReactiveObserverWithVM(reactiveObserver, vm);
6550
+ }
6551
+ return reactiveObserver;
6314
6552
  }
6315
6553
  function resetTemplateObserverAndUnsubscribe(vm) {
6316
6554
  const { tro, component } = vm;
@@ -6385,7 +6623,12 @@ function rerenderVM(vm) {
6385
6623
  }
6386
6624
  function connectRootElement(elm) {
6387
6625
  const vm = getAssociatedVM(elm);
6388
- logGlobalOperationStart(7 /* OperationId.GlobalHydrate */, vm);
6626
+ if (process.env.NODE_ENV !== 'production') {
6627
+ // Flush any logs for this VM so that the initial properties from the constructor don't "count"
6628
+ // in subsequent re-renders (lwc-rehydrate). Right now we're at the first render (lwc-hydrate).
6629
+ flushMutationLogsForVM(vm);
6630
+ }
6631
+ logGlobalOperationStartWithVM(7 /* OperationId.GlobalHydrate */, vm);
6389
6632
  // Usually means moving the element from one place to another, which is observable via
6390
6633
  // life-cycle hooks.
6391
6634
  if (vm.state === 1 /* VMState.connected */) {
@@ -6393,7 +6636,7 @@ function connectRootElement(elm) {
6393
6636
  }
6394
6637
  runConnectedCallback(vm);
6395
6638
  rehydrate(vm);
6396
- logGlobalOperationEnd(7 /* OperationId.GlobalHydrate */, vm);
6639
+ logGlobalOperationEndWithVM(7 /* OperationId.GlobalHydrate */, vm);
6397
6640
  }
6398
6641
  function disconnectRootElement(elm) {
6399
6642
  const vm = getAssociatedVM(elm);
@@ -6699,6 +6942,9 @@ function runRenderedCallback(vm) {
6699
6942
  }
6700
6943
  let rehydrateQueue = [];
6701
6944
  function flushRehydrationQueue() {
6945
+ // Gather the logs before rehydration starts so they can be reported at the end of rehydration.
6946
+ // Note that we also clear all existing logs at this point so that subsequent re-renders start from a clean slate.
6947
+ const mutationLogs = process.env.NODE_ENV !== 'production' ? getAndFlushMutationLogs() : undefined;
6702
6948
  logGlobalOperationStart(8 /* OperationId.GlobalRehydrate */);
6703
6949
  if (process.env.NODE_ENV !== 'production') {
6704
6950
  shared.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}.`);
@@ -6719,13 +6965,13 @@ function flushRehydrationQueue() {
6719
6965
  shared.ArrayUnshift.apply(rehydrateQueue, shared.ArraySlice.call(vms, i + 1));
6720
6966
  }
6721
6967
  // we need to end the measure before throwing.
6722
- logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */);
6968
+ logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */, mutationLogs);
6723
6969
  // re-throwing the original error will break the current tick, but since the next tick is
6724
6970
  // already scheduled, it should continue patching the rest.
6725
6971
  throw error;
6726
6972
  }
6727
6973
  }
6728
- logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */);
6974
+ logGlobalOperationEnd(8 /* OperationId.GlobalRehydrate */, mutationLogs);
6729
6975
  }
6730
6976
  function runConnectedCallback(vm) {
6731
6977
  const { state } = vm;
@@ -8136,5 +8382,5 @@ exports.swapTemplate = swapTemplate;
8136
8382
  exports.track = track;
8137
8383
  exports.unwrap = unwrap;
8138
8384
  exports.wire = wire;
8139
- /** version: 8.0.0-alpha.1 */
8385
+ /** version: 8.0.0 */
8140
8386
  //# sourceMappingURL=index.cjs.js.map