Skip to content

Commit 825a708

Browse files
committed
fix: ensure classic computed chains support (#8432)
* adds tests and gets tests passing lols * simplify working version * more fixes * reduce private api abuse * fixes * restore worse private api use :( * fix deprecation scenario * fix dep stripped * fix lint
1 parent 04674cd commit 825a708

File tree

9 files changed

+303
-15
lines changed

9 files changed

+303
-15
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ npm-debug.log*
2424
testem.log
2525
yarn-error.log
2626
.broccoli-cache
27+
tsconfig.tsbuildinfo
2728

2829
# Ignore artifacts of publishing
2930
*.tgz

packages/model/addon/-private/many-array.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { assert, deprecate } from '@ember/debug';
55

66
import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
77
import type Store from '@ember-data/store';
8-
import { IDENTIFIER_ARRAY_TAG, MUTATE, RecordArray, recordIdentifierFor, SOURCE } from '@ember-data/store/-private';
8+
import {
9+
IDENTIFIER_ARRAY_TAG,
10+
MUTATE,
11+
notifyArray,
12+
RecordArray,
13+
recordIdentifierFor,
14+
SOURCE,
15+
} from '@ember-data/store/-private';
916
import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class';
1017
import { IdentifierArrayCreateOptions } from '@ember-data/store/-private/record-arrays/identifier-array';
1118
import type { CreateRecordProperties } from '@ember-data/store/-private/store-service';
@@ -271,8 +278,9 @@ export default class RelatedCollection extends RecordArray {
271278

272279
notify() {
273280
const tag = this[IDENTIFIER_ARRAY_TAG];
274-
tag.ref = null;
275281
tag.shouldReset = true;
282+
// @ts-expect-error
283+
notifyArray(this);
276284
}
277285

278286
/**

packages/model/addon/-private/record-state.ts

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class Tag {
4242
this.rev = 1;
4343
this.isDirty = true;
4444
this.value = undefined;
45+
/*
46+
* whether this was part of a transaction when mutated
47+
*/
4548
this.t = false;
4649
}
4750
@tracked ref = null;

packages/private-build-infra/addon/current-deprecations.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@ export default {
5959
DEPRECATE_A_USAGE: '4.7',
6060
DEPRECATE_PROMISE_PROXIES: '4.7',
6161
DEPRECATE_ARRAY_LIKE: '4.7',
62-
DEPRECATE_COMPUTED_CHAINS: '4.7',
62+
DEPRECATE_COMPUTED_CHAINS: '5.0',
6363
DEPRECATE_NON_EXPLICIT_POLYMORPHISM: '4.7',
6464
};

packages/store/addon/-private/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export {
4646
default as RecordArray,
4747
default as IdentifierArray,
4848
Collection as AdapterPopulatedRecordArray,
49+
notifyArray,
4950
SOURCE,
5051
MUTATE,
5152
IDENTIFIER_ARRAY_TAG,

packages/store/addon/-private/managers/record-array-manager.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
@module @ember-data/store
33
*/
4-
import { addToTransaction } from '@ember-data/tracking/-private';
4+
import { addTransactionCB } from '@ember-data/tracking/-private';
55
import type { CollectionResourceDocument } from '@ember-data/types/q/ember-data-json-api';
66
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
77
import type { Dict } from '@ember-data/types/q/utils';
@@ -10,6 +10,8 @@ import IdentifierArray, {
1010
Collection,
1111
CollectionCreateOptions,
1212
IDENTIFIER_ARRAY_TAG,
13+
NOTIFY,
14+
notifyArray,
1315
SOURCE,
1416
} from '../record-arrays/identifier-array';
1517
import type Store from '../store-service';
@@ -175,9 +177,9 @@ class RecordArrayManager {
175177
let tag = array[IDENTIFIER_ARRAY_TAG];
176178
if (!tag.shouldReset) {
177179
tag.shouldReset = true;
178-
addToTransaction(tag);
179-
} else if (delta > 0 && tag.t) {
180-
addToTransaction(tag);
180+
addTransactionCB(array[NOTIFY]);
181+
} else if (delta > 0 && !tag.t) {
182+
addTransactionCB(array[NOTIFY]);
181183
}
182184
}
183185

@@ -241,7 +243,8 @@ class RecordArrayManager {
241243
const old = source.slice();
242244
source.length = 0;
243245
fastPush(source, identifiers);
244-
array[IDENTIFIER_ARRAY_TAG].ref = null;
246+
247+
notifyArray(array);
245248
array.meta = payload.meta || null;
246249
array.links = payload.links || null;
247250
array.isLoaded = true;

packages/store/addon/-private/record-arrays/identifier-array.ts

+47-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
/**
22
@module @ember-data/store
33
*/
4+
// @ts-expect-error
5+
import { tagForProperty } from '@ember/-internals/metal';
46
import { assert, deprecate } from '@ember/debug';
57
import { get, set } from '@ember/object';
68
import { dependentKeyCompat } from '@ember/object/compat';
79
import { compare } from '@ember/utils';
810
import { DEBUG } from '@glimmer/env';
911
import { tracked } from '@glimmer/tracking';
12+
// @ts-expect-error
13+
import { dirtyTag } from '@glimmer/validator';
1014
import Ember from 'ember';
1115

1216
import {
1317
DEPRECATE_A_USAGE,
1418
DEPRECATE_ARRAY_LIKE,
19+
DEPRECATE_COMPUTED_CHAINS,
1520
DEPRECATE_PROMISE_PROXIES,
1621
DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS,
1722
} from '@ember-data/private-build-infra/deprecations';
@@ -63,6 +68,18 @@ function isArraySetter(prop: KeyType): boolean {
6368
export const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
6469
export const SOURCE = Symbol('#source');
6570
export const MUTATE = Symbol('#update');
71+
export const NOTIFY = Symbol('#notify');
72+
73+
export function notifyArray(arr: IdentifierArray) {
74+
arr[IDENTIFIER_ARRAY_TAG].ref = null;
75+
76+
if (DEPRECATE_COMPUTED_CHAINS) {
77+
// eslint-disable-next-line
78+
dirtyTag(tagForProperty(arr, 'length'));
79+
// eslint-disable-next-line
80+
dirtyTag(tagForProperty(arr, '[]'));
81+
}
82+
}
6683

6784
function convertToInt(prop: KeyType): number | null {
6885
if (typeof prop === 'symbol') return null;
@@ -76,8 +93,16 @@ function convertToInt(prop: KeyType): number | null {
7693

7794
class Tag {
7895
@tracked ref = null;
79-
shouldReset: boolean = false;
80-
t = false;
96+
declare shouldReset: boolean;
97+
/*
98+
* whether this was part of a transaction when last mutated
99+
*/
100+
declare t: boolean;
101+
102+
constructor() {
103+
this.shouldReset = false;
104+
this.t = false;
105+
}
81106
}
82107

83108
type ProxiedMethod = (...args: unknown[]) => unknown;
@@ -127,11 +152,9 @@ interface PrivateState {
127152
@class RecordArray
128153
@public
129154
*/
130-
131-
interface IdentifierArray {
155+
interface IdentifierArray extends Omit<Array<RecordInstance>, '[]'> {
132156
[MUTATE]?(prop: string, args: unknown[], result?: unknown): void;
133157
}
134-
interface IdentifierArray extends Array<RecordInstance> {}
135158
class IdentifierArray {
136159
declare DEPRECATED_CLASS_NAME: string;
137160
/**
@@ -155,6 +178,9 @@ class IdentifierArray {
155178

156179
[IDENTIFIER_ARRAY_TAG] = new Tag();
157180
[SOURCE]: StableRecordIdentifier[];
181+
[NOTIFY]() {
182+
notifyArray(this);
183+
}
158184

159185
declare links: Links | PaginationLinks | null;
160186
declare meta: Dict<unknown> | null;
@@ -183,7 +209,7 @@ class IdentifierArray {
183209
// changing the reference breaks the Proxy
184210
// this[SOURCE] = [];
185211
this[SOURCE].length = 0;
186-
this[IDENTIFIER_ARRAY_TAG].ref = null;
212+
this[NOTIFY]();
187213
this.isDestroyed = true;
188214
}
189215

@@ -196,6 +222,14 @@ class IdentifierArray {
196222
this[SOURCE].length = value;
197223
}
198224

225+
// here to support computed chains
226+
// and {{#each}}
227+
get '[]'() {
228+
if (DEPRECATE_COMPUTED_CHAINS) {
229+
return this;
230+
}
231+
}
232+
199233
constructor(options: IdentifierArrayCreateOptions) {
200234
// eslint-disable-next-line @typescript-eslint/no-this-alias
201235
let self = this;
@@ -296,6 +330,10 @@ class IdentifierArray {
296330
}
297331
}
298332

333+
if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
334+
return self[prop];
335+
}
336+
299337
let fn = boundFns.get(prop);
300338
if (fn) return fn;
301339

@@ -403,6 +441,8 @@ class IdentifierArray {
403441
};
404442
}
405443

444+
this[NOTIFY] = this[NOTIFY].bind(proxy);
445+
406446
return proxy;
407447
}
408448

@@ -544,7 +584,7 @@ export class Collection extends IdentifierArray {
544584
Collection.prototype.query = null;
545585

546586
// Ensure instanceof works correctly
547-
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
587+
//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
548588

549589
if (DEPRECATE_ARRAY_LIKE) {
550590
IdentifierArray.prototype.DEPRECATED_CLASS_NAME = 'RecordArray';

packages/tracking/src/-private.ts

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function flushTransaction() {
3939
cb();
4040
});
4141
transaction.props.forEach((obj: Tag) => {
42+
// mark this mutation as part of a transaction
4243
obj.t = true;
4344
obj.ref = null;
4445
});
@@ -56,6 +57,7 @@ async function untrack() {
5657
cb();
5758
});
5859
transaction.props.forEach((obj: Tag) => {
60+
// mark this mutation as part of a transaction
5961
obj.t = true;
6062
obj.ref = null;
6163
});

0 commit comments

Comments
 (0)