Skip to content

Commit 96b2092

Browse files
author
James Fox
committed
add eslint + typescript + prettier support
1 parent 5652dc9 commit 96b2092

File tree

6 files changed

+942
-184
lines changed

6 files changed

+942
-184
lines changed

.eslintrc.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
parserOptions: {
3+
ecmaFeatures: {
4+
jsx: true,
5+
},
6+
ecmaVersion: 2017,
7+
sourceType: 'module',
8+
},
9+
extends: [
10+
'eslint:recommended',
11+
'plugin:@typescript-eslint/recommended',
12+
'prettier/@typescript-eslint',
13+
14+
// Prettier should always go last so it can trump any rules above
15+
'plugin:prettier/recommended',
16+
'prettier/react',
17+
],
18+
};

.prettierrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"printWidth": 120,
3+
"tabWidth": 2,
4+
"useTabs": false,
5+
"semi": true,
6+
"singleQuote": true,
7+
"trailingComma": "es5",
8+
"bracketSpacing": true,
9+
"jsxBracketSameLine": false
10+
}

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"dependencies": {
3030
"@optimizely/js-sdk-logging": "^0.1.0",
3131
"@optimizely/optimizely-sdk": "3.6.0-alpha.1",
32-
"@types/react-dom": "^16.9.5",
3332
"hoist-non-react-statics": "^3.3.0",
3433
"prop-types": "^15.6.2",
3534
"utility-types": "^2.1.0"
@@ -44,9 +43,17 @@
4443
"@types/jest": "^23.3.12",
4544
"@types/prop-types": "^15.5.6",
4645
"@types/react": "^16.7.18",
46+
"@types/react-dom": "^16.9.5",
47+
"@typescript-eslint/eslint-plugin": "^2.23.0",
48+
"@typescript-eslint/parser": "^2.23.0",
4749
"enzyme": "^3.8.0",
4850
"enzyme-adapter-react-16": "^1.7.1",
51+
"eslint": "^6.8.0",
52+
"eslint-config-prettier": "^6.10.0",
53+
"eslint-plugin-prettier": "^3.1.2",
54+
"eslint-plugin-react": "^7.19.0",
4955
"jest": "^23.6.0",
56+
"prettier": "1.19.1",
5057
"react": "^16.8.0",
5158
"react-dom": "^16.8.0",
5259
"rollup": "^1.1.0",

src/hooks.ts

Lines changed: 61 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,69 +18,67 @@ import * as optimizely from '@optimizely/optimizely-sdk';
1818
import { getLogger } from '@optimizely/js-sdk-logging';
1919

2020
import { setupAutoUpdateListeners } from './autoUpdate';
21-
import { VariableValuesObject, OnReadyResult } from './client'
21+
import { VariableValuesObject, OnReadyResult } from './client';
2222
import { OptimizelyContext } from './Context';
2323

2424
const useFeatureLogger = getLogger('useFeature');
2525

2626
type UseFeatureState = {
27-
isEnabled: boolean,
28-
variables: VariableValuesObject,
27+
isEnabled: boolean;
28+
variables: VariableValuesObject;
2929
};
3030

3131
type ClientReady = boolean;
3232
type DidTimeout = boolean;
3333

3434
type UseFeatureOptions = {
35-
autoUpdate?: boolean,
36-
timeout?: number
35+
autoUpdate?: boolean;
36+
timeout?: number;
3737
};
3838

3939
type UseFeatureOverrides = {
40-
overrideUserId?: string,
41-
overrideAttributes?: optimizely.UserAttributes,
42-
}
40+
overrideUserId?: string;
41+
overrideAttributes?: optimizely.UserAttributes;
42+
};
4343

4444
interface UseFeature {
45-
(
46-
featureKey: string,
47-
options?: UseFeatureOptions,
48-
overrides?: UseFeatureOverrides,
49-
): [
50-
UseFeatureState["isEnabled"],
51-
UseFeatureState["variables"],
45+
(featureKey: string, options?: UseFeatureOptions, overrides?: UseFeatureOverrides): [
46+
UseFeatureState['isEnabled'],
47+
UseFeatureState['variables'],
5248
ClientReady,
5349
DidTimeout
54-
]
50+
];
5551
}
5652

5753
/**
5854
* A React Hook that retrieves the status of a feature flag and its variables, optionally
5955
* auto updating those values based on underlying user or datafile changes.
60-
*
61-
* Note: The react client can become ready AFTER the timeout period.
56+
*
57+
* Note: The react client can become ready AFTER the timeout period.
6258
* ClientReady and DidTimeout provide signals to handle this scenario.
6359
*/
64-
export const useFeature : UseFeature = (featureKey, options = {}, overrides = {}) => {
60+
export const useFeature: UseFeature = (featureKey, options = {}, overrides = {}) => {
6561
const { isServerSide, optimizely, timeout } = useContext(OptimizelyContext);
6662
if (!optimizely) {
6763
throw new Error('optimizely prop must be supplied via a parent <OptimizelyProvider>');
6864
}
69-
const finalReadyTimeout: number | undefined =
70-
options.timeout !== undefined ? options.timeout : timeout;
65+
const finalReadyTimeout: number | undefined = options.timeout !== undefined ? options.timeout : timeout;
7166

7267
// Helper function to return the current values for isEnabled and variables.
73-
const getCurrentValues = useCallback(() => ({
74-
isEnabled: optimizely.isFeatureEnabled(featureKey, overrides.overrideUserId, overrides.overrideAttributes),
75-
variables: optimizely.getFeatureVariables(featureKey, overrides.overrideUserId, overrides.overrideAttributes),
76-
}), [featureKey, overrides]);
68+
const getCurrentValues = useCallback(
69+
() => ({
70+
isEnabled: optimizely.isFeatureEnabled(featureKey, overrides.overrideUserId, overrides.overrideAttributes),
71+
variables: optimizely.getFeatureVariables(featureKey, overrides.overrideUserId, overrides.overrideAttributes),
72+
}),
73+
[featureKey, overrides]
74+
);
7775

7876
// Set the initial state immediately serverSide
79-
const [ data, setData ] = useState<UseFeatureState>(() => {
77+
const [data, setData] = useState<UseFeatureState>(() => {
8078
if (isServerSide) {
8179
return getCurrentValues();
8280
}
83-
return { isEnabled: false, variables: {}};
81+
return { isEnabled: false, variables: {} };
8482
});
8583

8684
const [clientReady, setClientReady] = useState(isServerSide ? true : false);
@@ -89,53 +87,48 @@ export const useFeature : UseFeature = (featureKey, options = {}, overrides = {}
8987
useEffect(() => {
9088
const cleanupFns: Array<() => void> = [];
9189

92-
optimizely.onReady({ timeout: finalReadyTimeout }).then((res: OnReadyResult) => {
93-
if (res.success) {
94-
// didTimeout=false
95-
useFeatureLogger.info(`feature="${featureKey}" successfully set for user="${optimizely.user.id}"`);
96-
return;
97-
}
98-
setDidTimeout(true);
99-
useFeatureLogger.info(
100-
`feature="${featureKey}" could not be set before timeout of ${finalReadyTimeout}ms, reason="${res.reason || ''}"`,
101-
);
102-
// Since we timed out, wait for the dataReadyPromise to resolve before setting up.
103-
return res.dataReadyPromise!.then(
104-
() => {
105-
useFeatureLogger.info(
106-
`feature="${featureKey}" is now set, but after timeout.`,
107-
);
108-
});
109-
})
110-
.then(() => {
111-
setClientReady(true);
112-
setData(getCurrentValues());
113-
if (options.autoUpdate) {
114-
cleanupFns.push(
115-
setupAutoUpdateListeners(optimizely, 'feature', featureKey, useFeatureLogger, () => {
116-
if (cleanupFns.length) {
117-
setData(getCurrentValues());
118-
}
119-
})
90+
optimizely
91+
.onReady({ timeout: finalReadyTimeout })
92+
.then((res: OnReadyResult) => {
93+
if (res.success) {
94+
// didTimeout=false
95+
useFeatureLogger.info(`feature="${featureKey}" successfully set for user="${optimizely.user.id}"`);
96+
return;
97+
}
98+
setDidTimeout(true);
99+
useFeatureLogger.info(
100+
`feature="${featureKey}" could not be set before timeout of ${finalReadyTimeout}ms, reason="${res.reason ||
101+
''}"`
120102
);
121-
}
122-
})
123-
.catch(() => {
124-
/* The user promise or core client promise rejected. */
125-
useFeatureLogger.error(`Error initializing client. The core client or user promise(s) rejected.`);
126-
})
103+
// Since we timed out, wait for the dataReadyPromise to resolve before setting up.
104+
return res.dataReadyPromise!.then(() => {
105+
useFeatureLogger.info(`feature="${featureKey}" is now set, but after timeout.`);
106+
});
107+
})
108+
.then(() => {
109+
setClientReady(true);
110+
setData(getCurrentValues());
111+
if (options.autoUpdate) {
112+
cleanupFns.push(
113+
setupAutoUpdateListeners(optimizely, 'feature', featureKey, useFeatureLogger, () => {
114+
if (cleanupFns.length) {
115+
setData(getCurrentValues());
116+
}
117+
})
118+
);
119+
}
120+
})
121+
.catch(() => {
122+
/* The user promise or core client promise rejected. */
123+
useFeatureLogger.error(`Error initializing client. The core client or user promise(s) rejected.`);
124+
});
127125

128126
return () => {
129-
while(cleanupFns.length) {
127+
while (cleanupFns.length) {
130128
cleanupFns.shift()!();
131129
}
132130
};
133131
}, [optimizely]);
134132

135-
return [
136-
data.isEnabled,
137-
data.variables,
138-
clientReady,
139-
didTimeout,
140-
];
133+
return [data.isEnabled, data.variables, clientReady, didTimeout];
141134
};

src/utils.tsx

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,64 +14,62 @@
1414
* limitations under the License.
1515
*/
1616

17-
import hoistNonReactStatics from 'hoist-non-react-statics'
18-
import * as optimizely from '@optimizely/optimizely-sdk'
19-
import * as React from 'react'
17+
import hoistNonReactStatics from 'hoist-non-react-statics';
18+
import * as optimizely from '@optimizely/optimizely-sdk';
19+
import * as React from 'react';
2020

2121
type User = {
22-
id: string
23-
attributes: optimizely.UserAttributes
24-
}
22+
id: string;
23+
attributes: optimizely.UserAttributes;
24+
};
2525

2626
export function areUsersEqual(user1: User, user2: User): boolean {
2727
if (user1.id !== user2.id) {
28-
return false
28+
return false;
2929
}
3030

31-
const user1keys = Object.keys(user1.attributes)
32-
const user2keys = Object.keys(user2.attributes)
33-
user1keys.sort()
34-
user2keys.sort()
31+
const user1keys = Object.keys(user1.attributes);
32+
const user2keys = Object.keys(user2.attributes);
33+
user1keys.sort();
34+
user2keys.sort();
3535

36-
const areKeysLenEqual = user1keys.length === user2keys.length
36+
const areKeysLenEqual = user1keys.length === user2keys.length;
3737
if (!areKeysLenEqual) {
38-
return false
38+
return false;
3939
}
4040

4141
for (let i = 0; i < user1keys.length; i++) {
42-
const key1 = user1keys[i]
43-
const key2 = user2keys[i]
42+
const key1 = user1keys[i];
43+
const key2 = user2keys[i];
4444
if (key1 !== key2) {
45-
return false
45+
return false;
4646
}
4747

4848
if (user1.attributes[key1] !== user2.attributes[key2]) {
49-
return false
49+
return false;
5050
}
5151
}
5252

53-
return true
53+
return true;
5454
}
5555

5656
export interface AcceptsForwardedRef<R> {
57-
forwardedRef?: React.Ref<R>
57+
forwardedRef?: React.Ref<R>;
5858
}
5959

6060
export function hoistStaticsAndForwardRefs<R, P extends AcceptsForwardedRef<R>>(
6161
Target: React.ComponentType<P>,
6262
Source: React.ComponentType<any>,
63-
displayName: string,
63+
displayName: string
6464
): React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>> {
6565
// Make sure to hoist statics and forward any refs through from Source to Target
6666
// From the React docs:
6767
// https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
6868
// https://reactjs.org/docs/forwarding-refs.html#forwarding-refs-in-higher-order-components
69-
const forwardRef: React.RefForwardingComponent<R, P> = (props, ref) => (
70-
<Target {...props} forwardedRef={ref} />
71-
)
72-
forwardRef.displayName = `${displayName}(${Source.displayName || Source.name})`
69+
const forwardRef: React.RefForwardingComponent<R, P> = (props, ref) => <Target {...props} forwardedRef={ref} />;
70+
forwardRef.displayName = `${displayName}(${Source.displayName || Source.name})`;
7371
return hoistNonReactStatics<
7472
React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>,
7573
React.ComponentType<any>
76-
>(React.forwardRef(forwardRef), Source)
74+
>(React.forwardRef(forwardRef), Source);
7775
}

0 commit comments

Comments
 (0)