Skip to content

Commit

Permalink
feat: convert to typescript, fix cacheConfig (#620)
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed Apr 7, 2022
1 parent dd57db9 commit a478d51
Show file tree
Hide file tree
Showing 13 changed files with 5,057 additions and 647 deletions.
1 change: 1 addition & 0 deletions .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = (api) => ({
includePolyfills: 'usage-pure',
},
],
'@babel/typescript',
],
plugins: [api.env() !== 'esm' && 'add-module-exports'].filter(Boolean),
});
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
module.exports = {
extends: ['4catalyzer-react', '4catalyzer-jest', 'prettier'],
extends: [
'4catalyzer-react',
'4catalyzer-typescript',
'4catalyzer-jest',
'prettier',
],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
Expand Down
68 changes: 37 additions & 31 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
],
"main": "lib/index.js",
"module": "es/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "npm run build:cjs && npm run build:esm",
"build:cjs": "babel -d lib --delete-dir-on-start src",
"build:esm": "babel --env-name esm -d es --delete-dir-on-start src",
"build": "4c build src",
"build-fixtures": "npm run update-schema && npm run relay-compiler",
"format": "eslint --fix . && npm run prettier -- --write",
"lint": "eslint . && npm run prettier -- -l",
Expand All @@ -23,10 +22,8 @@
"testonly": "jest --runInBand --verbose",
"update-schema": "babel-node test/fixtures/updateSchema.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.js": "eslint --fix",
Expand Down Expand Up @@ -64,52 +61,61 @@
},
"homepage": "https://github.com/relay-tools/found-relay#readme",
"dependencies": {
"@babel/runtime": "^7.13.9",
"@babel/runtime": "^7.17.9",
"@types/react-relay": "^11.0.1",
"@types/relay-runtime": "^12.0.0",
"dequal": "^2.0.2",
"is-promise": "^4.0.0",
"lodash": "^4.17.21",
"prop-types": "^15.7.2",
"warning": "^4.0.3"
},
"peerDependencies": {
"found": ">=0.5.1",
"react": ">=16.9.0",
"react-relay": ">=9.0.0",
"relay-runtime": ">=9.0.0"
"react-relay": ">=12.0.0",
"relay-runtime": ">=12.0.0"
},
"devDependencies": {
"@4c/babel-preset": "^8.1.1",
"@babel/cli": "^7.14.5",
"@babel/core": "^7.14.6",
"@babel/node": "^7.14.7",
"@4c/cli": "^3.0.1",
"@4c/tsconfig": "^0.4.0",
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-typescript": "^7.16.7",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"babel-jest": "^26.6.3",
"babel-plugin-add-module-exports": "^1.0.4",
"babel-plugin-relay": "^10.1.3",
"codecov": "^3.8.2",
"codecov": "^3.8.3",
"delay": "^5.0.0",
"doctoc": "^2.0.1",
"eslint": "^7.29.0",
"eslint-config-4catalyzer-jest": "^2.1.0",
"eslint-config-4catalyzer-react": "^1.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"doctoc": "^2.1.0",
"eslint": "^8.12.0",
"eslint-config-4catalyzer-jest": "^2.2.0",
"eslint-config-4catalyzer-react": "^1.2.2",
"eslint-config-4catalyzer-typescript": "^3.2.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.1.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"farce": "^0.4.5",
"found": "^0.6.0",
"graphql": "^15.5.1",
"husky": "^4.3.8",
"hookem": "^2.0.1",
"jest": "^26.6.3",
"lint-staged": "^11.0.0",
"p-defer": "^3.0.0",
"prettier": "^2.3.2",
"prettier": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-relay": "^10.1.3",
"relay-compiler": "^10.1.3",
"react-relay": "^12.0.0",
"relay-compiler": "^12.0.0",
"relay-local-schema": "^0.8.0",
"relay-runtime": "^10.1.3"
"relay-runtime": "^12.0.0",
"typescript": "^4.6.3"
}
}
88 changes: 73 additions & 15 deletions src/QuerySubscription.js → src/QuerySubscription.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,68 @@
import { createOperationDescriptor, getRequest } from 'relay-runtime';
import {
CacheConfig,
Disposable,
Environment,
FetchPolicy,
GraphQLTaggedNode,
SelectorData,
Snapshot,
Subscription,
Variables,
createOperationDescriptor,
getRequest,
} from 'relay-runtime';

export interface QuerySubscriptionOptions {
environment: Environment;
query: GraphQLTaggedNode;
variables: Variables;
cacheConfig?: CacheConfig;
fetchPolicy?: FetchPolicy;
}

export interface ReadyState {
error: Error | null;
props: SelectorData | null;
retry: (() => void) | null;
}
export default class QuerySubscription {
constructor({ environment, query, variables, cacheConfig, fetchPolicy }) {
environment: Environment;

query: GraphQLTaggedNode;

variables: Variables;

cacheConfig: CacheConfig | null | undefined;

fetchPolicy: FetchPolicy | null | undefined;

operation: any;

fetchPromise: Promise<void> | null;

selectionReference: Disposable | null;

pendingRequest: Subscription | null;

rootSubscription: Disposable | null;

retrying: boolean;

retryingAfterError: boolean;

readyState: ReadyState;

listeners: (() => void)[];

relayContext: { environment: any; variables: any };

constructor({
environment,
query,
variables,
cacheConfig,
fetchPolicy,
}: QuerySubscriptionOptions) {
this.environment = environment;
this.query = query;
this.variables = variables;
Expand Down Expand Up @@ -42,8 +103,8 @@ export default class QuerySubscription {
return this.fetchPromise;
}

execute(resolve) {
let snapshot;
execute(resolve: () => void) {
let snapshot: Snapshot | undefined;

this.selectionReference = this.retain();

Expand All @@ -52,10 +113,7 @@ export default class QuerySubscription {
return;
}

snapshot = this.environment.lookup(
this.operation.fragment,
this.operation,
);
snapshot = this.environment.lookup(this.operation.fragment);

this.onChange(snapshot);

Expand All @@ -67,7 +125,7 @@ export default class QuerySubscription {
resolve();
};

const onError = (error) => {
const onError = (error: Error) => {
this.updateReadyState({
error,
props: null,
Expand All @@ -88,7 +146,6 @@ export default class QuerySubscription {
this.pendingRequest = this.environment
.execute({
operation: this.operation,
cacheConfig: this.cacheConfig,
})
.finally(() => {
this.pendingRequest = null;
Expand All @@ -97,7 +154,7 @@ export default class QuerySubscription {
next: onSnapshot,
error: onError,
});
} catch (error) {
} catch (error: any) {
onError(error);
return;
}
Expand All @@ -118,27 +175,27 @@ export default class QuerySubscription {
}
}

updateReadyState(readyState) {
updateReadyState(readyState: ReadyState) {
this.readyState = readyState;

this.listeners.forEach((listener) => {
listener();
});
}

onChange = (snapshot) => {
onChange = (snapshot: Snapshot) => {
this.updateReadyState({
error: null,
props: snapshot.data,
retry: this.retry,
});
};

subscribe(listener) {
subscribe(listener: () => void) {
this.listeners.push(listener);
}

unsubscribe(listener) {
unsubscribe(listener: () => void) {
this.listeners = this.listeners.filter((item) => item !== listener);
}

Expand All @@ -147,6 +204,7 @@ export default class QuerySubscription {
this.retryingAfterError = !!this.readyState.error;

this.dispose();
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.execute(() => {});
};

Expand Down
69 changes: 41 additions & 28 deletions src/ReadyStateRenderer.js → src/ReadyStateRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import PropTypes from 'prop-types';
/* eslint-disable @typescript-eslint/ban-types */
import type { Match } from 'found';
import React from 'react';
import { ReactRelayContext } from 'react-relay';
import warning from 'warning';
import type { Disposable } from 'relay-runtime';

import QuerySubscription from './QuerySubscription';
import renderElement from './renderElement';

const { hasOwnProperty } = Object.prototype;

const propTypes = {
match: PropTypes.shape({
route: PropTypes.object.isRequired,
}).isRequired,
Component: PropTypes.elementType,
isComponentResolved: PropTypes.bool.isRequired,
hasComponent: PropTypes.bool.isRequired,
element: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
routeChildren: PropTypes.oneOfType([PropTypes.object, PropTypes.element]),
querySubscription: PropTypes.instanceOf(QuerySubscription).isRequired,
fetched: PropTypes.bool.isRequired,
};

class ReadyStateRenderer extends React.Component {
constructor(props) {
type RouteElement =
| React.ReactElement
| ((routes: React.ReactElement | {}) => React.ReactElement);

interface Props {
match: Match & { route: Record<string, any> };
Component: React.ElementType | null;
isComponentResolved: boolean;
hasComponent: boolean;
element: RouteElement;
routeChildren: React.ReactElement | {};
querySubscription: QuerySubscription;
fetched: boolean;

[other: string]: any;
}

interface State {
isInitialRender: true;
element: RouteElement;
propsElement: RouteElement;
querySubscription: QuerySubscription;
selectionReference: Disposable;
onUpdate: () => void;
}
class ReadyStateRenderer extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

const { element, querySubscription } = props;
Expand All @@ -41,12 +54,15 @@ class ReadyStateRenderer extends React.Component {
this.props.querySubscription.subscribe(this.onUpdate);
}

static getDerivedStateFromProps({ element, querySubscription }, state) {
static getDerivedStateFromProps(
{ element, querySubscription }: Props,
state: State,
) {
if (state.isInitialRender) {
return { isInitialRender: false };
}

let nextState = null;
let nextState: Partial<State> | null = null;

if (element !== state.propsElement) {
nextState = {
Expand Down Expand Up @@ -126,13 +142,12 @@ class ReadyStateRenderer extends React.Component {
// At least on Node v8.x, it's slightly faster to guard the delete here
// with this hasOwnProperty check.
if (hasOwnProperty.call(ownProps, relayPropName)) {
warning(
false,
'Ignoring <ReadyStateRenderer> prop `%s` that shadows a Relay prop from its query `%s`. This is most likely due to its parent cloning it and adding extraneous Relay props.',
relayPropName,
querySubscription.getQueryName(),
);

if (process.env.NODE_ENV !== 'production') {
console.error(
`Ignoring <ReadyStateRenderer> prop \`${relayPropName}\` that shadows a Relay prop from its query \`${querySubscription.getQueryName()}\`. This is most likely due to its parent cloning it and adding extraneous Relay props.`,
);
}
// @ts-ignore
delete ownProps[relayPropName];
}
});
Expand All @@ -153,6 +168,4 @@ class ReadyStateRenderer extends React.Component {
}
}

ReadyStateRenderer.propTypes = propTypes;

export default ReadyStateRenderer;
Loading

0 comments on commit a478d51

Please sign in to comment.