Skip to content

Commit e5d70c3

Browse files
FoHoOVRich-Harris
andauthored
fix: improved fine-grainability of ReactiveDate (#12110)
* fix: improve ReactiveDate class * improved test * added changeset * improved performance by using less signals * using the same signal for getX and getUTCX * fixed types * combined setX with setUTCX tests together * simplified fine-grainability by using deriveds * reused the calculated new_time * removed logic that reduced number of signals, cuz its use-cases are rare * inference is fine * tidy up * tweaks * more efficient iteration * tweak * more descriptive changeset * this makes all tests but one pass * fix * adjust test to be robust across timezones * tweak more tests * Update .changeset/sleepy-dogs-sit.md --------- Co-authored-by: Rich Harris <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent 730e179 commit e5d70c3

File tree

5 files changed

+607
-82
lines changed

5 files changed

+607
-82
lines changed

.changeset/sleepy-dogs-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: lazily create a derived for each read method on `SvelteDate.prototype`

packages/svelte/src/internal/client/runtime.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ export function deep_read(value, visited = new Set()) {
11381138
!visited.has(value)
11391139
) {
11401140
visited.add(value);
1141-
// When working with a possible ReactiveDate, this
1141+
// When working with a possible SvelteDate, this
11421142
// will ensure we capture changes to it.
11431143
if (value instanceof Date) {
11441144
value.getTime();
Lines changed: 40 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,62 @@
1+
/** @import { Source } from '#client' */
2+
import { derived } from '../internal/client/index.js';
13
import { source, set } from '../internal/client/reactivity/sources.js';
24
import { get } from '../internal/client/runtime.js';
35

4-
/** @type {Array<keyof Date>} */
5-
const read = [
6-
'getDate',
7-
'getDay',
8-
'getFullYear',
9-
'getHours',
10-
'getMilliseconds',
11-
'getMinutes',
12-
'getMonth',
13-
'getSeconds',
14-
'getTime',
15-
'getTimezoneOffset',
16-
'getUTCDate',
17-
'getUTCDay',
18-
'getUTCFullYear',
19-
'getUTCHours',
20-
'getUTCMilliseconds',
21-
'getUTCMinutes',
22-
'getUTCMonth',
23-
'getUTCSeconds',
24-
// @ts-expect-error this is deprecated
25-
'getYear',
26-
'toDateString',
27-
'toISOString',
28-
'toJSON',
29-
'toLocaleDateString',
30-
'toLocaleString',
31-
'toLocaleTimeString',
32-
'toString',
33-
'toTimeString',
34-
'toUTCString'
35-
];
36-
37-
/** @type {Array<keyof Date>} */
38-
const write = [
39-
'setDate',
40-
'setFullYear',
41-
'setHours',
42-
'setMilliseconds',
43-
'setMinutes',
44-
'setMonth',
45-
'setSeconds',
46-
'setTime',
47-
'setUTCDate',
48-
'setUTCFullYear',
49-
'setUTCHours',
50-
'setUTCMilliseconds',
51-
'setUTCMinutes',
52-
'setUTCMonth',
53-
'setUTCSeconds',
54-
// @ts-expect-error this is deprecated
55-
'setYear'
56-
];
57-
586
var inited = false;
597

608
export class SvelteDate extends Date {
61-
#raw_time = source(super.getTime());
9+
#time = source(super.getTime());
10+
11+
/** @type {Map<keyof Date, Source<unknown>>} */
12+
#deriveds = new Map();
13+
14+
/** @param {any[]} params */
15+
constructor(...params) {
16+
// @ts-ignore
17+
super(...params);
18+
if (!inited) this.#init();
19+
}
6220

63-
// We init as part of the first instance so that we can treeshake this class
6421
#init() {
65-
if (!inited) {
66-
inited = true;
67-
const proto = SvelteDate.prototype;
68-
const date_proto = Date.prototype;
22+
inited = true;
23+
24+
var proto = SvelteDate.prototype;
25+
var date_proto = Date.prototype;
6926

70-
for (const method of read) {
27+
var methods = /** @type {Array<keyof Date & string>} */ (
28+
Object.getOwnPropertyNames(date_proto)
29+
);
30+
31+
for (const method of methods) {
32+
if (method.startsWith('get') || method.startsWith('to')) {
7133
// @ts-ignore
7234
proto[method] = function (...args) {
73-
get(this.#raw_time);
74-
// @ts-ignore
75-
return date_proto[method].apply(this, args);
35+
var d = this.#deriveds.get(method);
36+
37+
if (d === undefined) {
38+
d = derived(() => {
39+
get(this.#time);
40+
// @ts-ignore
41+
return date_proto[method].apply(this, args);
42+
});
43+
44+
this.#deriveds.set(method, d);
45+
}
46+
47+
return get(d);
7648
};
7749
}
7850

79-
for (const method of write) {
51+
if (method.startsWith('set')) {
8052
// @ts-ignore
8153
proto[method] = function (...args) {
8254
// @ts-ignore
83-
const v = date_proto[method].apply(this, args);
84-
const time = date_proto.getTime.call(this);
85-
if (time !== this.#raw_time.v) {
86-
set(this.#raw_time, time);
87-
}
88-
return v;
55+
var result = date_proto[method].apply(this, args);
56+
set(this.#time, date_proto.getTime.call(this));
57+
return result;
8958
};
9059
}
9160
}
9261
}
93-
94-
/**
95-
* @param {any[]} values
96-
*/
97-
constructor(...values) {
98-
// @ts-ignore
99-
super(...values);
100-
this.#init();
101-
}
10262
}

0 commit comments

Comments
 (0)