Skip to content

Commit 8e74bcd

Browse files
committed
it works, now for memory leak testing
1 parent 54c2413 commit 8e74bcd

File tree

3 files changed

+61
-28
lines changed

3 files changed

+61
-28
lines changed

ember-resources/src/intermediate-representation.ts

+27-26
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { assert } from '@ember/debug';
44
import { associateDestroyableChild, destroy, registerDestructor } from '@ember/destroyable';
55
// @ts-ignore
66
import { invokeHelper, setHelperManager } from '@ember/helper';
7-
import { dependencySatisfies, importSync, macroCondition } from '@embroider/macros';
87

98
import { ReadonlyCell } from './cell.ts';
109
import { ResourceManagerFactory } from './resource-manager.ts';
11-
import { CURRENT, INTERNAL } from './types.ts';
10+
import { INTERNAL } from './types.ts';
1211
import { registerUsable, TYPE_KEY } from './use.ts';
1312
import { shallowFlat } from './utils.ts';
1413

@@ -19,20 +18,10 @@ export const CREATE_KEY = Symbol.for('__configured-resource-key__');
1918
export const DEBUG_NAME = Symbol.for('DEBUG_NAME');
2019
export const RESOURCE_CACHE = Symbol.for('__resource_cache__');
2120

22-
let getOwner: (context: unknown) => Owner | undefined;
23-
let setOwner: (context: unknown, owner: Owner) => void;
24-
25-
if (macroCondition(dependencySatisfies('ember-source', '>=4.12.0'))) {
26-
// In no version of ember where `@ember/owner` tried to be imported did it exist
27-
// if (macroCondition(false)) {
28-
// Using 'any' here because importSync can't lookup types correctly
29-
getOwner = (importSync('@ember/owner') as any).getOwner;
30-
setOwner = (importSync('@ember/owner') as any).setOwner;
31-
} else {
32-
// Using 'any' here because importSync can't lookup types correctly
33-
getOwner = (importSync('@ember/application') as any).getOwner;
34-
setOwner = (importSync('@ember/application') as any).setOwner;
35-
}
21+
import { compatOwner } from './owner.ts';
22+
23+
const getOwner = compatOwner.getOwner;
24+
const setOwner = compatOwner.setOwner;
3625

3726
/**
3827
* The return value from resource()
@@ -71,16 +60,17 @@ export class Builder<Value> {
7160
this.#fn = fn;
7261
}
7362

74-
create(owner: Owner) {
75-
return new Resource(this.#fn, owner);
63+
create() {
64+
return new Resource(this.#fn);
7665
}
7766
}
7867

7968
const TYPE = 'function-based';
8069

8170
registerUsable(TYPE, (context: object, config: Builder<unknown>) => {
82-
let owner = getOwner(context) || context;
83-
let instance = config.create(owner as Owner);
71+
let instance = config.create();
72+
73+
instance.link(context);
8474

8575
return instance[RESOURCE_CACHE];
8676
});
@@ -90,12 +80,12 @@ registerUsable(TYPE, (context: object, config: Builder<unknown>) => {
9080
*/
9181
export class Resource<Value> {
9282
#originalFn: ResourceFunction<Value>;
93-
#owner: Owner;
83+
#owner: Owner | undefined;
9484
#previousFn: object | undefined;
9585
#usableCache = new WeakMap<object, ReturnType<typeof invokeHelper>>();
9686
#cache: ReturnType<typeof invokeHelper>;
9787

98-
constructor(fn: ResourceFunction<Value>, owner: Owner) {
88+
constructor(fn: ResourceFunction<Value>) {
9989
/**
10090
* We have to copy the `fn` in case there are multiple
10191
* usages or invocations of the function.
@@ -104,7 +94,6 @@ export class Resource<Value> {
10494
* destroy.
10595
*/
10696
this.#originalFn = fn.bind(null);
107-
this.#owner = owner;
10897

10998
this.#cache = createCache(() => {
11099
if (this.#previousFn) {
@@ -116,6 +105,11 @@ export class Resource<Value> {
116105
associateDestroyableChild(this.#originalFn, currentFn);
117106
this.#previousFn = currentFn;
118107

108+
assert(
109+
`Cannot create a resource without an owner. Must have previously called .link()`,
110+
this.#owner,
111+
);
112+
119113
let maybeValue = currentFn({
120114
on: {
121115
cleanup: (destroyer: Destructor) => {
@@ -161,11 +155,18 @@ export class Resource<Value> {
161155

162156
return maybeValue;
163157
});
164-
165-
setOwner(this.#cache, this.#owner);
166158
}
167-
link(owner: Owner) {
159+
link(context: object) {
160+
let owner = getOwner(context);
161+
162+
assert(`Cannot link without an owner`, owner);
163+
168164
this.#owner = owner;
165+
166+
associateDestroyableChild(context, this.#cache);
167+
associateDestroyableChild(context, this.#originalFn);
168+
169+
setOwner(this.#cache, this.#owner);
169170
}
170171

171172
get [RESOURCE_CACHE](): unknown {

ember-resources/src/owner.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { dependencySatisfies, importSync, macroCondition } from '@embroider/macros';
2+
3+
import type Owner from '@ember/owner';
4+
5+
interface CompatOwner {
6+
getOwner: (context: unknown) => Owner | undefined;
7+
setOwner: (context: unknown, owner: Owner) => void;
8+
}
9+
10+
export const compatOwner = {} as CompatOwner;
11+
12+
if (macroCondition(dependencySatisfies('ember-source', '>=4.12.0'))) {
13+
// In no version of ember where `@ember/owner` tried to be imported did it exist
14+
// if (macroCondition(false)) {
15+
// Using 'any' here because importSync can't lookup types correctly
16+
compatOwner.getOwner = (importSync('@ember/owner') as any).getOwner;
17+
compatOwner.setOwner = (importSync('@ember/owner') as any).setOwner;
18+
} else {
19+
// Using 'any' here because importSync can't lookup types correctly
20+
compatOwner.getOwner = (importSync('@ember/application') as any).getOwner;
21+
compatOwner.setOwner = (importSync('@ember/application') as any).setOwner;
22+
}

ember-resources/src/resource-manager.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { assert } from '@ember/debug';
22
// @ts-ignore
33
import { capabilities as helperCapabilities } from '@ember/helper';
44

5+
import { compatOwner } from './owner.ts';
6+
57
import type { Builder, Resource } from './intermediate-representation.ts';
68
import type Owner from '@ember/owner';
79

10+
const setOwner = compatOwner.setOwner;
11+
812
/**
913
* Note, a function-resource receives on object, hooks.
1014
* We have to build that manually in this helper manager
@@ -15,14 +19,20 @@ class FunctionResourceManager<Value> {
1519
hasDestroyable: true,
1620
});
1721

18-
constructor(protected owner: Owner) {}
22+
constructor(protected owner: Owner) {
23+
setOwner(this, owner);
24+
}
1925

2026
/**
2127
* Resources do not take args.
2228
* However, they can access tracked data
2329
*/
2430
createHelper(builder: Builder<Value>): Resource<Value> {
25-
return builder.create(this.owner);
31+
let instance = builder.create();
32+
33+
instance.link(this);
34+
35+
return instance;
2636
}
2737

2838
getValue(state: Resource<Value>) {

0 commit comments

Comments
 (0)