@isograph/react 0.0.0-main-3779371d → 0.0.0-main-adc27d88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/IsographEnvironment.d.ts +6 -1
- package/dist/IsographEnvironment.js +11 -1
- package/dist/cache.d.ts +2 -2
- package/dist/cache.js +66 -12
- package/dist/componentCache.d.ts +1 -1
- package/dist/componentCache.js +11 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/read.d.ts +7 -3
- package/dist/read.js +40 -17
- package/dist/useRerenderWhenEncounteredRecordChanges.d.ts +2 -0
- package/dist/useRerenderWhenEncounteredRecordChanges.js +14 -0
- package/dist/useResult.js +4 -9
- package/package.json +4 -4
- package/src/IsographEnvironment.tsx +17 -1
- package/src/cache.ts +78 -13
- package/src/componentCache.ts +24 -11
- package/src/index.ts +1 -0
- package/src/read.ts +55 -18
- package/src/tests/__isograph/Query/meName/entrypoint.ts +43 -0
- package/src/tests/__isograph/Query/meName/reader.ts +40 -0
- package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +79 -0
- package/src/tests/__isograph/Query/meNameSuccessor/reader.ts +67 -0
- package/src/tests/__isograph/Query/nodeField/entrypoint.ts +42 -0
- package/src/tests/__isograph/Query/nodeField/reader.ts +45 -0
- package/src/tests/__isograph/iso.ts +60 -0
- package/src/tests/garbageCollection.test.ts +130 -0
- package/src/tests/isograph.config.json +7 -0
- package/src/tests/meNameSuccessor.ts +20 -0
- package/src/tests/nodeQuery.ts +17 -0
- package/src/tests/schema.graphql +16 -0
- package/src/tests/tsconfig.json +21 -0
- package/src/useRerenderWhenEncounteredRecordChanges.ts +15 -0
- package/src/useResult.ts +8 -9
- package/tsconfig.pkg.json +2 -1
@@ -0,0 +1,42 @@
|
|
1
|
+
import type {IsographEntrypoint, NormalizationAst, RefetchQueryArtifactWrapper} from '@isograph/react';
|
2
|
+
import type {Query__nodeField__param, Query__nodeField__outputType} from './reader';
|
3
|
+
import readerResolver from './reader';
|
4
|
+
const nestedRefetchQueries: RefetchQueryArtifactWrapper[] = [];
|
5
|
+
|
6
|
+
const queryText = 'query nodeField ($id: ID!) {\
|
7
|
+
node____id___v_id: node(id: $id) {\
|
8
|
+
id,\
|
9
|
+
},\
|
10
|
+
}';
|
11
|
+
|
12
|
+
const normalizationAst: NormalizationAst = [
|
13
|
+
{
|
14
|
+
kind: "Linked",
|
15
|
+
fieldName: "node",
|
16
|
+
arguments: [
|
17
|
+
[
|
18
|
+
"id",
|
19
|
+
{ kind: "Variable", name: "id" },
|
20
|
+
],
|
21
|
+
],
|
22
|
+
selections: [
|
23
|
+
{
|
24
|
+
kind: "Scalar",
|
25
|
+
fieldName: "id",
|
26
|
+
arguments: null,
|
27
|
+
},
|
28
|
+
],
|
29
|
+
},
|
30
|
+
];
|
31
|
+
const artifact: IsographEntrypoint<
|
32
|
+
Query__nodeField__param,
|
33
|
+
Query__nodeField__outputType
|
34
|
+
> = {
|
35
|
+
kind: "Entrypoint",
|
36
|
+
queryText,
|
37
|
+
normalizationAst,
|
38
|
+
nestedRefetchQueries,
|
39
|
+
readerArtifact: readerResolver,
|
40
|
+
};
|
41
|
+
|
42
|
+
export default artifact;
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import type {ReaderArtifact, ReaderAst, ExtractSecondParam} from '@isograph/react';
|
2
|
+
import { nodeField as resolver } from '../../../nodeQuery.ts';
|
3
|
+
|
4
|
+
// the type, when read out (either via useLazyReference or via graph)
|
5
|
+
export type Query__nodeField__outputType = ReturnType<typeof resolver>;
|
6
|
+
|
7
|
+
const readerAst: ReaderAst<Query__nodeField__param> = [
|
8
|
+
{
|
9
|
+
kind: "Linked",
|
10
|
+
fieldName: "node",
|
11
|
+
alias: null,
|
12
|
+
arguments: [
|
13
|
+
[
|
14
|
+
"id",
|
15
|
+
{ kind: "Variable", name: "id" },
|
16
|
+
],
|
17
|
+
],
|
18
|
+
selections: [
|
19
|
+
{
|
20
|
+
kind: "Scalar",
|
21
|
+
fieldName: "id",
|
22
|
+
alias: null,
|
23
|
+
arguments: null,
|
24
|
+
},
|
25
|
+
],
|
26
|
+
},
|
27
|
+
];
|
28
|
+
|
29
|
+
export type Query__nodeField__param = {
|
30
|
+
node: ({
|
31
|
+
id: string,
|
32
|
+
} | null),
|
33
|
+
};
|
34
|
+
|
35
|
+
const artifact: ReaderArtifact<
|
36
|
+
Query__nodeField__param,
|
37
|
+
Query__nodeField__outputType
|
38
|
+
> = {
|
39
|
+
kind: "ReaderArtifact",
|
40
|
+
resolver: resolver as any,
|
41
|
+
readerAst,
|
42
|
+
variant: { kind: "Eager" },
|
43
|
+
};
|
44
|
+
|
45
|
+
export default artifact;
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import type {IsographEntrypoint} from '@isograph/react';
|
2
|
+
import { Query__meNameSuccessor__param } from './Query/meNameSuccessor/reader'
|
3
|
+
import { Query__meName__param } from './Query/meName/reader'
|
4
|
+
import { Query__nodeField__param } from './Query/nodeField/reader'
|
5
|
+
import entrypoint_Query__meNameSuccessor from '../__isograph/Query/meNameSuccessor/entrypoint'
|
6
|
+
import entrypoint_Query__meName from '../__isograph/Query/meName/entrypoint'
|
7
|
+
import entrypoint_Query__nodeField from '../__isograph/Query/nodeField/entrypoint'
|
8
|
+
|
9
|
+
type IdentityWithParam<TParam> = <TResolverReturn>(
|
10
|
+
x: (param: TParam) => TResolverReturn
|
11
|
+
) => (param: TParam) => TResolverReturn;
|
12
|
+
type IdentityWithParamComponent<TParam> = <TResolverReturn, TSecondParam = Record<string, never>>(
|
13
|
+
x: (data: TParam, secondParam: TSecondParam) => TResolverReturn
|
14
|
+
) => (data: TParam, secondParam: TSecondParam) => TResolverReturn;
|
15
|
+
|
16
|
+
type WhitespaceCharacter = ' ' | '\t' | '\n';
|
17
|
+
type Whitespace<In> = In extends `${WhitespaceCharacter}${infer In}`
|
18
|
+
? Whitespace<In>
|
19
|
+
: In;
|
20
|
+
|
21
|
+
type MatchesWhitespaceAndString<
|
22
|
+
TString extends string,
|
23
|
+
T
|
24
|
+
> = Whitespace<T> extends `${TString}${string}` ? T : never;
|
25
|
+
|
26
|
+
export function iso<T>(
|
27
|
+
param: T & MatchesWhitespaceAndString<'field Query.meNameSuccessor', T>
|
28
|
+
): IdentityWithParam<Query__meNameSuccessor__param>;
|
29
|
+
|
30
|
+
export function iso<T>(
|
31
|
+
param: T & MatchesWhitespaceAndString<'field Query.meName', T>
|
32
|
+
): IdentityWithParam<Query__meName__param>;
|
33
|
+
|
34
|
+
export function iso<T>(
|
35
|
+
param: T & MatchesWhitespaceAndString<'field Query.nodeField', T>
|
36
|
+
): IdentityWithParam<Query__nodeField__param>;
|
37
|
+
|
38
|
+
export function iso<T>(
|
39
|
+
param: T & MatchesWhitespaceAndString<'entrypoint Query.meNameSuccessor', T>
|
40
|
+
): typeof entrypoint_Query__meNameSuccessor;
|
41
|
+
|
42
|
+
export function iso<T>(
|
43
|
+
param: T & MatchesWhitespaceAndString<'entrypoint Query.meName', T>
|
44
|
+
): typeof entrypoint_Query__meName;
|
45
|
+
|
46
|
+
export function iso<T>(
|
47
|
+
param: T & MatchesWhitespaceAndString<'entrypoint Query.nodeField', T>
|
48
|
+
): typeof entrypoint_Query__nodeField;
|
49
|
+
|
50
|
+
export function iso(_isographLiteralText: string):
|
51
|
+
| IdentityWithParam<any>
|
52
|
+
| IdentityWithParamComponent<any>
|
53
|
+
| IsographEntrypoint<any, any>
|
54
|
+
{
|
55
|
+
return function identity<TResolverReturn>(
|
56
|
+
clientFieldOrEntrypoint: (param: any) => TResolverReturn,
|
57
|
+
): (param: any) => TResolverReturn {
|
58
|
+
return clientFieldOrEntrypoint;
|
59
|
+
};
|
60
|
+
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
2
|
+
import { ROOT_ID, createIsographEnvironment } from '../IsographEnvironment';
|
3
|
+
import { garbageCollectEnvironment, retainQuery } from '../garbageCollection';
|
4
|
+
import { iso } from './__isograph/iso';
|
5
|
+
import { nodeFieldRetainedQuery } from './nodeQuery';
|
6
|
+
|
7
|
+
const getDefaultStore = () => ({
|
8
|
+
[ROOT_ID]: {
|
9
|
+
me: { __link: '0' },
|
10
|
+
you: { __link: '1' },
|
11
|
+
node____id___0: {
|
12
|
+
__link: '0',
|
13
|
+
},
|
14
|
+
},
|
15
|
+
0: {
|
16
|
+
__typename: 'Economist',
|
17
|
+
id: '0',
|
18
|
+
name: 'Jeremy Bentham',
|
19
|
+
successor: { __link: '1' },
|
20
|
+
},
|
21
|
+
1: {
|
22
|
+
__typename: 'Economist',
|
23
|
+
id: '1',
|
24
|
+
name: 'John Stuart Mill',
|
25
|
+
predecessor: { __link: '0' },
|
26
|
+
successor: { __link: '2' },
|
27
|
+
},
|
28
|
+
2: {
|
29
|
+
__typename: 'Economist',
|
30
|
+
id: '2',
|
31
|
+
name: 'Henry Sidgwick',
|
32
|
+
predecessor: { __link: '1' },
|
33
|
+
},
|
34
|
+
});
|
35
|
+
|
36
|
+
export const meNameField = iso(`
|
37
|
+
field Query.meName {
|
38
|
+
me {
|
39
|
+
name
|
40
|
+
}
|
41
|
+
}
|
42
|
+
`)(() => {});
|
43
|
+
import meNameEntrypoint from './__isograph/Query/meName/entrypoint';
|
44
|
+
import { meNameSuccessorRetainedQuery } from './meNameSuccessor';
|
45
|
+
iso(`entrypoint Query.meName`);
|
46
|
+
const meNameRetainedQuery = {
|
47
|
+
normalizationAst: meNameEntrypoint.normalizationAst,
|
48
|
+
variables: {},
|
49
|
+
};
|
50
|
+
|
51
|
+
describe('garbage collection', () => {
|
52
|
+
test('Unreferenced records should be garbage collected', () => {
|
53
|
+
const store = getDefaultStore();
|
54
|
+
const environment = createIsographEnvironment(
|
55
|
+
store,
|
56
|
+
null as any,
|
57
|
+
null as any,
|
58
|
+
);
|
59
|
+
|
60
|
+
expect(store[1]).not.toBe(undefined);
|
61
|
+
|
62
|
+
// TODO enable babel so we don't have to do this
|
63
|
+
retainQuery(environment, meNameRetainedQuery);
|
64
|
+
garbageCollectEnvironment(environment);
|
65
|
+
|
66
|
+
expect(store[1]).toBe(undefined);
|
67
|
+
});
|
68
|
+
|
69
|
+
test('Referenced records should not be garbage collected', () => {
|
70
|
+
const store = getDefaultStore();
|
71
|
+
const environment = createIsographEnvironment(
|
72
|
+
store,
|
73
|
+
null as any,
|
74
|
+
null as any,
|
75
|
+
);
|
76
|
+
|
77
|
+
expect(store[0]).not.toBe(undefined);
|
78
|
+
|
79
|
+
// TODO enable babel so we don't have to do this
|
80
|
+
retainQuery(environment, meNameRetainedQuery);
|
81
|
+
garbageCollectEnvironment(environment);
|
82
|
+
|
83
|
+
expect(store[0]).not.toBe(undefined);
|
84
|
+
});
|
85
|
+
|
86
|
+
test('Referenced records should not be garbage collected, and this should work with variables', () => {
|
87
|
+
const store = getDefaultStore();
|
88
|
+
const environment = createIsographEnvironment(
|
89
|
+
store,
|
90
|
+
null as any,
|
91
|
+
null as any,
|
92
|
+
);
|
93
|
+
|
94
|
+
expect(store[0]).not.toBe(undefined);
|
95
|
+
|
96
|
+
retainQuery(environment, nodeFieldRetainedQuery);
|
97
|
+
garbageCollectEnvironment(environment);
|
98
|
+
|
99
|
+
expect(store[0]).not.toBe(undefined);
|
100
|
+
});
|
101
|
+
|
102
|
+
test('Referenced records should not be garbage collected, and this should work through multiple levels', () => {
|
103
|
+
const store = getDefaultStore();
|
104
|
+
const environment = createIsographEnvironment(
|
105
|
+
store,
|
106
|
+
null as any,
|
107
|
+
null as any,
|
108
|
+
);
|
109
|
+
|
110
|
+
retainQuery(environment, meNameSuccessorRetainedQuery);
|
111
|
+
garbageCollectEnvironment(environment);
|
112
|
+
|
113
|
+
expect(store[0]).not.toBe(undefined);
|
114
|
+
expect(store[1]).not.toBe(undefined);
|
115
|
+
expect(store[2]).not.toBe(undefined);
|
116
|
+
});
|
117
|
+
|
118
|
+
test('ROOT_ID should not be garbage collected, even if there are no retained queries', () => {
|
119
|
+
const store = getDefaultStore();
|
120
|
+
const environment = createIsographEnvironment(
|
121
|
+
store,
|
122
|
+
null as any,
|
123
|
+
null as any,
|
124
|
+
);
|
125
|
+
garbageCollectEnvironment(environment);
|
126
|
+
|
127
|
+
expect(store[ROOT_ID]).not.toBe(undefined);
|
128
|
+
expect(store[0]).toBe(undefined);
|
129
|
+
});
|
130
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { iso } from './__isograph/iso';
|
2
|
+
|
3
|
+
export const meNameField = iso(`
|
4
|
+
field Query.meNameSuccessor {
|
5
|
+
me {
|
6
|
+
name
|
7
|
+
successor {
|
8
|
+
successor {
|
9
|
+
name
|
10
|
+
}
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
`)(() => {});
|
15
|
+
import meNameSuccessorEntrypoint from './__isograph/Query/meNameSuccessor/entrypoint';
|
16
|
+
iso(`entrypoint Query.meNameSuccessor`);
|
17
|
+
export const meNameSuccessorRetainedQuery = {
|
18
|
+
normalizationAst: meNameSuccessorEntrypoint.normalizationAst,
|
19
|
+
variables: {},
|
20
|
+
};
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { iso } from './__isograph/iso';
|
2
|
+
|
3
|
+
// TODO investigate why this can't be in garbageCollection.test.ts without
|
4
|
+
// typescript incorrectly thinking it is referenced in its own initializer
|
5
|
+
export const nodeField = iso(`
|
6
|
+
field Query.nodeField($id: ID!) {
|
7
|
+
node(id: $id) {
|
8
|
+
id
|
9
|
+
}
|
10
|
+
}
|
11
|
+
`)(() => {});
|
12
|
+
import nodeFieldEntrypoint from './__isograph/Query/nodeField/entrypoint';
|
13
|
+
iso(`entrypoint Query.nodeField`);
|
14
|
+
export const nodeFieldRetainedQuery = {
|
15
|
+
normalizationAst: nodeFieldEntrypoint.normalizationAst,
|
16
|
+
variables: { id: 0 },
|
17
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "es5",
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
5
|
+
"allowJs": true,
|
6
|
+
"skipLibCheck": true,
|
7
|
+
"strict": true,
|
8
|
+
"noEmit": true,
|
9
|
+
"esModuleInterop": true,
|
10
|
+
"module": "esnext",
|
11
|
+
"moduleResolution": "bundler",
|
12
|
+
"resolveJsonModule": true,
|
13
|
+
"isolatedModules": true,
|
14
|
+
"jsx": "preserve",
|
15
|
+
"incremental": true,
|
16
|
+
"allowImportingTsExtensions": true,
|
17
|
+
"paths": {
|
18
|
+
"@isograph/react": ["../index.ts"],
|
19
|
+
},
|
20
|
+
},
|
21
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { useEffect, useState } from 'react';
|
2
|
+
import { DataId, IsographEnvironment } from './IsographEnvironment';
|
3
|
+
import { subscribe } from './cache';
|
4
|
+
|
5
|
+
export function useRerenderWhenEncounteredRecordChanges(
|
6
|
+
environment: IsographEnvironment,
|
7
|
+
encounteredRecords: Set<DataId>,
|
8
|
+
) {
|
9
|
+
const [, setState] = useState<object | void>();
|
10
|
+
useEffect(() => {
|
11
|
+
return subscribe(environment, encounteredRecords, () => {
|
12
|
+
return setState({});
|
13
|
+
});
|
14
|
+
}, []);
|
15
|
+
}
|
package/src/useResult.ts
CHANGED
@@ -1,20 +1,19 @@
|
|
1
|
-
import { useEffect, useState } from 'react';
|
2
1
|
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
3
|
-
import { subscribe } from './cache';
|
4
2
|
import { read } from './read';
|
5
3
|
import { FragmentReference } from './FragmentReference';
|
4
|
+
import { useRerenderWhenEncounteredRecordChanges } from './useRerenderWhenEncounteredRecordChanges';
|
6
5
|
|
7
6
|
export function useResult<TReadFromStore extends Object, TClientFieldValue>(
|
8
7
|
fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>,
|
9
8
|
): TClientFieldValue {
|
10
9
|
const environment = useIsographEnvironment();
|
11
10
|
|
12
|
-
const
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
});
|
17
|
-
}, []);
|
11
|
+
const { item: data, encounteredRecords } = read(
|
12
|
+
environment,
|
13
|
+
fragmentReference,
|
14
|
+
);
|
18
15
|
|
19
|
-
|
16
|
+
useRerenderWhenEncounteredRecordChanges(environment, encounteredRecords);
|
17
|
+
|
18
|
+
return data;
|
20
19
|
}
|