Skip to content

Commit

Permalink
Fix triple-nested composed resources
Browse files Browse the repository at this point in the history
  • Loading branch information
NullVoxPopuli committed Jul 26, 2023
1 parent 48f981c commit cc363c2
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
14 changes: 0 additions & 14 deletions ember-resources/src/core/class-based/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,6 @@ import { Resource } from './resource';
import type { ArgsWrapper } from '[core-types]';
import type Owner from '@ember/owner';

/**
*
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
// export interface Resource<T extends ArgsWrapper = ArgsWrapper> extends InstanceType<
// HelperLike<{
// Args: {
// Named: NonNullable<T['named']>;
// Positional: NonNullable<T['positional']>
// };
// // Return: number
// }>
// > {}

class ResourceManager {
capabilities = helperCapabilities('3.23', {
hasValue: true,
Expand Down
25 changes: 20 additions & 5 deletions ember-resources/src/core/function-based/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,29 @@ import { dependencySatisfies, importSync, macroCondition } from '@embroider/macr
import { ReadonlyCell } from '../../util/cell';
import { CURRENT, INTERNAL } from './types';

import type { Cache, Destructor, InternalFunctionResourceConfig, ResourceFunction } from './types';
import type {
Cache,
Destructor,
InternalFunctionResourceConfig,
Reactive,
ResourceFunction,
} from './types';
import type { ResourceAPI } from './types';
import type Owner from '@ember/owner';

let getOwner: (context: unknown) => Owner | undefined;
let setOwner: (context: unknown, owner: Owner) => void;

if (macroCondition(dependencySatisfies('ember-source', '>=4.12.0'))) {
// In no version of ember where `@ember/owner` tried to be imported did it exist
// if (macroCondition(false)) {
// Using 'any' here because importSync can't lookup types correctly
getOwner = (importSync('@ember/owner') as any).getOwner;
setOwner = (importSync('@ember/owner') as any).setOwner;
} else {
// Using 'any' here because importSync can't lookup types correctly
getOwner = (importSync('@ember/application') as any).getOwner;
setOwner = (importSync('@ember/application') as any).setOwner;
}

/**
Expand Down Expand Up @@ -87,11 +96,11 @@ class FunctionResourceManager {
destroy(previousCache);
}

let cache = invokeHelper(owner, usable);
let nestedCache = invokeHelper(cache, usable);

associateDestroyableChild(currentFn, cache as object);
associateDestroyableChild(currentFn, nestedCache as object);

usableCache.set(usable, cache);
usableCache.set(usable, nestedCache);

return new ReadonlyCell(() => {
let cache = usableCache.get(usable);
Expand All @@ -113,6 +122,8 @@ class FunctionResourceManager {
return maybeValue;
});

setOwner(cache, owner);

return { fn: thisFn, cache };
}

Expand All @@ -123,7 +134,7 @@ class FunctionResourceManager {
return maybeValue();
}

if (CURRENT in maybeValue) {
if (isReactive(maybeValue)) {
return maybeValue[CURRENT];
}

Expand All @@ -135,4 +146,8 @@ class FunctionResourceManager {
}
}

function isReactive<Value>(maybe: unknown): maybe is Reactive<Value> {
return typeof maybe === 'object' && maybe !== null && CURRENT in maybe;
}

export const ResourceManagerFactory = (owner: Owner) => new FunctionResourceManager(owner);
25 changes: 25 additions & 0 deletions test-app/tests/core/function-resource/composition-test.gts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ module('Core | (function) resource | use | rendering', function (hooks) {
assert.dom().hasText('1');
});

test('it deeply works with directly returning the resource', async function (assert) {
let controlledCount = cell(0);

const Count = resource(() => {
return controlledCount;
});

const AlsoCount = resource(({ use }) => {
return use(Count);
});

const DeeplyCount = resource(({ use }) => {
return use(AlsoCount);
});

await render(<template>{{DeeplyCount}}</template>);

assert.dom().hasText('0');

controlledCount.current++;
await settled();

assert.dom().hasText('1');
});

test('it works with the blueprint/factory', async function (assert) {
let nowDate = Date.now();
let format = (time: Reactive<number>) => formatter.format(time.current);
Expand Down

0 comments on commit cc363c2

Please sign in to comment.