Skip to content

Commit 476226e

Browse files
authored
Merge pull request #103 from pzuraq/refactor-to-use-storage-primitives
Refactor to use TrackedStorage
2 parents d9040a0 + 593b061 commit 476226e

File tree

10 files changed

+3026
-350
lines changed

10 files changed

+3026
-350
lines changed

.eslintrc.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ module.exports = {
1111
}
1212
},
1313
plugins: [
14-
'ember'
14+
'ember',
15+
'@typescript-eslint'
1516
],
1617
extends: [
1718
'eslint:recommended',
18-
'plugin:ember/recommended'
19+
'plugin:ember/recommended',
1920
],
2021
env: {
2122
browser: true
@@ -54,6 +55,24 @@ module.exports = {
5455
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
5556
// add your custom rules and overrides for node files here
5657
})
58+
},
59+
// TS
60+
{
61+
files: ['**/*.ts'],
62+
extends: [
63+
'plugin:@typescript-eslint/recommended',
64+
],
65+
rules: {
66+
'@typescript-eslint/ban-types': [
67+
'error',
68+
{
69+
extendDefaults: true,
70+
types: {
71+
object: false,
72+
}
73+
}
74+
]
75+
}
5776
}
5877
]
5978
};

.github/workflows/CI.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
runs-on: ubuntu-latest
3131
strategy:
3232
matrix:
33-
node-version: ['10', '12', '14']
33+
node-version: ['12', '14']
3434

3535
steps:
3636
- uses: actions/checkout@v2
@@ -62,7 +62,7 @@ jobs:
6262
strategy:
6363
matrix:
6464
ember-version:
65-
['lts-3.16', 'lts-3.20', 'lts-3.24', 'release', 'beta', 'canary']
65+
['lts-3.24', 'release', 'beta', 'canary']
6666

6767
steps:
6868
- uses: actions/checkout@v2

config/ember-try.js

-16
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,6 @@ module.exports = async function () {
1313
return {
1414
useYarn: true,
1515
scenarios: [
16-
{
17-
name: 'ember-lts-3.16',
18-
npm: {
19-
devDependencies: {
20-
'ember-source': '~3.16.10',
21-
},
22-
},
23-
},
24-
{
25-
name: 'ember-lts-3.20',
26-
npm: {
27-
devDependencies: {
28-
'ember-source': '~3.20.6',
29-
},
30-
},
31-
},
3216
{
3317
name: 'ember-lts-3.24',
3418
npm: {

package.json

+7-3
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
"prepare": "yarn build:ts"
3434
},
3535
"dependencies": {
36+
"@ember/test-helpers": "^2.3.0",
3637
"@glimmer/tracking": "^1.0.0",
37-
"ember-cli-babel": "^7.26.5"
38+
"ember-cli-babel": "^7.26.5",
39+
"qunit": "^2.16.0"
3840
},
3941
"devDependencies": {
4042
"@ember/optional-features": "^2.0.0",
@@ -44,6 +46,7 @@
4446
"@types/ember__test-helpers": "^2.0.0",
4547
"@types/qunit": "^2.11.1",
4648
"@types/rsvp": "^4.0.3",
49+
"@typescript-eslint/eslint-plugin": "^4.29.0",
4750
"@typescript-eslint/parser": "^4.23.0",
4851
"broccoli-asset-rev": "^3.0.0",
4952
"ember-cli": "~3.27.0",
@@ -58,10 +61,11 @@
5861
"ember-export-application-global": "^2.0.1",
5962
"ember-load-initializers": "^2.1.1",
6063
"ember-maybe-import-regenerator": "^0.1.6",
61-
"ember-qunit": "^4.6.0",
64+
"ember-qunit": "^5.1.4",
6265
"ember-resolver": "^8.0.2",
6366
"ember-source": "~3.27.1",
6467
"ember-source-channel-url": "^3.0.0",
68+
"ember-tracked-storage-polyfill": "1.0.0",
6569
"ember-try": "^1.4.0",
6670
"ember-welcome-page": "^4.0.0",
6771
"eslint": "^7.0.0",
@@ -75,7 +79,7 @@
7579
"typescript": "4.3.5"
7680
},
7781
"engines": {
78-
"node": "10.* || >= 12"
82+
"node": "12.* || >= 14"
7983
},
8084
"ember": {
8185
"edition": "octane"

src/-private/map.ts

+135-63
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,187 @@
11
import {
2-
consumeKey,
3-
consumeCollection,
4-
dirtyKey,
5-
dirtyCollection
6-
} from './util';
2+
TrackedStorage,
3+
createStorage,
4+
getValue,
5+
setValue,
6+
} from 'ember-tracked-storage-polyfill';
7+
8+
export class TrackedMap<K = unknown, V = unknown> implements Map<K, V> {
9+
private collection = createStorage(null, () => false);
10+
11+
private storages: Map<K, TrackedStorage<null>> = new Map();
12+
13+
private vals: Map<K, V>;
14+
15+
private readStorageFor(key: K): void {
16+
const { storages } = this;
17+
let storage = storages.get(key);
18+
19+
if (storage === undefined) {
20+
storage = createStorage(null, () => false);
21+
storages.set(key, storage);
22+
}
23+
24+
getValue(storage);
25+
}
26+
27+
private dirtyStorageFor(key: K): void {
28+
const storage = this.storages.get(key);
29+
30+
if (storage) {
31+
setValue(storage, null);
32+
}
33+
}
34+
35+
constructor(entries?: readonly (readonly [K, V])[] | null) {
36+
this.vals = new Map(entries);
37+
}
738

8-
export class TrackedMap<K = unknown, V = unknown> extends Map<K, V> {
939
// **** KEY GETTERS ****
10-
get(key: K) {
11-
consumeKey(this, key);
40+
get(key: K): V | undefined {
41+
// entangle the storage for the key
42+
this.readStorageFor(key);
1243

13-
return super.get(key);
44+
return this.vals.get(key);
1445
}
1546

16-
has(key: K) {
17-
consumeKey(this, key);
47+
has(key: K): boolean {
48+
this.readStorageFor(key);
1849

19-
return super.has(key);
50+
return this.vals.has(key);
2051
}
2152

2253
// **** ALL GETTERS ****
23-
entries() {
24-
consumeCollection(this);
54+
entries(): IterableIterator<[K, V]> {
55+
getValue(this.collection);
2556

26-
return super.entries();
57+
return this.vals.entries();
2758
}
2859

29-
keys() {
30-
consumeCollection(this);
60+
keys(): IterableIterator<K> {
61+
getValue(this.collection);
3162

32-
return super.keys();
63+
return this.vals.keys();
3364
}
3465

35-
values() {
36-
consumeCollection(this);
66+
values(): IterableIterator<V> {
67+
getValue(this.collection);
3768

38-
return super.values();
69+
return this.vals.values();
3970
}
4071

41-
forEach(fn: (value: V, key: K, map: this) => void) {
42-
consumeCollection(this);
72+
forEach(fn: (value: V, key: K, map: Map<K, V>) => void): void {
73+
getValue(this.collection);
4374

44-
super.forEach(fn);
75+
this.vals.forEach(fn);
4576
}
4677

47-
get size() {
48-
consumeCollection(this);
78+
get size(): number {
79+
getValue(this.collection);
80+
81+
return this.vals.size;
82+
}
83+
84+
[Symbol.iterator](): IterableIterator<[K, V]> {
85+
getValue(this.collection);
86+
87+
return this.vals[Symbol.iterator]();
88+
}
4989

50-
return super.size;
90+
get [Symbol.toStringTag](): string {
91+
return this.vals[Symbol.toStringTag];
5192
}
5293

5394
// **** KEY SETTERS ****
54-
set(key: K, value: V) {
55-
dirtyKey(this, key);
56-
dirtyCollection(this);
95+
set(key: K, value: V): this {
96+
this.dirtyStorageFor(key);
97+
setValue(this.collection, null);
98+
99+
this.vals.set(key, value);
57100

58-
return super.set(key, value);
101+
return this;
59102
}
60103

61-
delete(key: K) {
62-
dirtyKey(this, key);
63-
dirtyCollection(this);
104+
delete(key: K): boolean {
105+
this.dirtyStorageFor(key);
106+
setValue(this.collection, null);
64107

65-
return super.delete(key);
108+
return this.vals.delete(key);
66109
}
67110

68111
// **** ALL SETTERS ****
69-
clear() {
70-
super.forEach((_v, k) => dirtyKey(this, k));
71-
dirtyCollection(this);
112+
clear(): void {
113+
this.storages.forEach((s) => setValue(s, null));
114+
setValue(this.collection, null);
72115

73-
return super.clear();
116+
this.vals.clear();
74117
}
75118
}
76119

77-
if (typeof Symbol !== undefined) {
78-
let originalIterator = TrackedMap.prototype[Symbol.iterator];
120+
// So instanceof works
121+
Object.setPrototypeOf(TrackedMap, Map.prototype);
122+
123+
export class TrackedWeakMap<
124+
K extends object = object,
125+
V = unknown
126+
> implements WeakMap<K, V> {
127+
private storages: WeakMap<K, TrackedStorage<null>> = new WeakMap();
128+
129+
private vals: WeakMap<K, V>;
130+
131+
private readStorageFor(key: K): void {
132+
const { storages } = this;
133+
let storage = storages.get(key);
79134

80-
Object.defineProperty(TrackedMap.prototype, Symbol.iterator, {
81-
get() {
82-
consumeCollection(this);
83-
return originalIterator;
135+
if (storage === undefined) {
136+
storage = createStorage(null, () => false);
137+
storages.set(key, storage);
84138
}
85-
});
86-
}
87139

88-
export class TrackedWeakMap<K extends object = object, V = unknown> extends WeakMap<
89-
K,
90-
V
91-
> {
92-
get(key: K) {
93-
consumeKey(this, key);
140+
getValue(storage);
141+
}
142+
143+
private dirtyStorageFor(key: K): void {
144+
const storage = this.storages.get(key);
145+
146+
if (storage) {
147+
setValue(storage, null);
148+
}
149+
}
94150

95-
return super.get(key);
151+
constructor(iterable?: (readonly [K, V][] | null)) {
152+
this.vals = new WeakMap(iterable);
96153
}
97154

98-
has(key: K) {
99-
consumeKey(this, key);
155+
get(key: K): V | undefined {
156+
this.readStorageFor(key);
100157

101-
return super.has(key);
158+
return this.vals.get(key);
102159
}
103160

104-
set(key: K, value: V) {
105-
dirtyKey(this, key);
161+
has(key: K): boolean {
162+
this.readStorageFor(key);
106163

107-
return super.set(key, value);
164+
return this.vals.has(key);
108165
}
109166

110-
delete(key: K) {
111-
dirtyKey(this, key);
167+
set(key: K, value: V): this {
168+
this.dirtyStorageFor(key);
169+
170+
this.vals.set(key, value);
112171

113-
return super.delete(key);
172+
return this;
173+
}
174+
175+
delete(key: K): boolean {
176+
this.dirtyStorageFor(key);
177+
178+
return this.vals.delete(key);
179+
}
180+
181+
get [Symbol.toStringTag](): string {
182+
return this.vals[Symbol.toStringTag];
114183
}
115184
}
185+
186+
// So instanceof works
187+
Object.setPrototypeOf(TrackedWeakMap, WeakMap.prototype);

0 commit comments

Comments
 (0)