init: the first public commit for AFFiNE

This commit is contained in:
DarkSky
2022-07-22 15:49:21 +08:00
commit e3e3741393
1451 changed files with 108124 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
@@ -0,0 +1,18 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
+55
View File
@@ -0,0 +1,55 @@
# Feature Flags
## Usage
- set provider
```tsx
import { FeatureFlagsProvider } from '@toeverything/datasource/feature-flags';
const App = () => {
return (
<FeatureFlagsProvider>
<Page />
</FeatureFlagsProvider>
);
};
```
- use flag
```ts
import { useFlag } from '@toeverything/datasource/feature-flags';
const App = () => {
// flag key, default value
const flag1 = useFlag('flag1', false);
// ^? boolean
const flag2 = useFlag<boolean>('flag2');
// ^? boolean | undefined
return flag1 && <div>The flag1 is enable!</div>;
};
```
## How to add a new feature flag?
- Enter [Portal](https://portal.featureflag.co/switch-manage)
- Click `New Switch` button
- Input `Switch Name`, and Enter
- Adjust data and save
# Test flags
**When entering development mode feature flag will NOT be updated in real time**
- `activateFfcDevMode()` play with feature flags locally
- `quitFfcDevMode()` quit dev mode
## Running unit tests
Run `nx test datasource/feature-flags` to execute the unit tests via [Jest](https://jestjs.io).
## References
- [Feature Flag Portal](https://portal.featureflag.co/)
- [Feature Flag React Web APP DEMO](https://featureflag.moyincloud.com/ksrm/React_Web_APP.html)
@@ -0,0 +1,10 @@
/* eslint-disable */
export default {
displayName: 'datasource-feature-flags',
preset: '../../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../../coverage/libs/datasource/feature-flags',
};
@@ -0,0 +1,8 @@
{
"name": "@toeverything/datasource/feature-flags",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"ffc-js-client-side-sdk": "^1.1.4"
}
}
@@ -0,0 +1,45 @@
{
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/datasource/feature-flags/src",
"projectType": "library",
"tags": ["library:feature-flags"],
"targets": {
"build": {
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/datasource/feature-flags",
"tsConfig": "libs/datasource/feature-flags/tsconfig.lib.json",
"project": "libs/datasource/feature-flags/package.json",
"entryFile": "libs/datasource/feature-flags/src/index.ts",
"external": ["react/jsx-runtime"],
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
"glob": "libs/datasource/feature-flags/README.md",
"input": ".",
"output": "."
}
]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"libs/datasource/feature-flags/**/*.{ts,tsx,js,jsx}"
]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/libs/datasource/feature-flags"],
"options": {
"jestConfig": "libs/datasource/feature-flags/jest.config.ts",
"passWithNoTests": true
}
}
}
}
@@ -0,0 +1,63 @@
import ffcClient, { Ffc } from 'ffc-js-client-side-sdk';
import type {
IFeatureFlagSet,
IOption,
} from 'ffc-js-client-side-sdk/esm/types';
import { createContext, ReactNode, useEffect, useState } from 'react';
import { config } from './config';
/**
* Init `ffcClient`
* Ported from https://github.com/feature-flags-co/ffc-js-client-side-sdk-react-jotai-demo/blob/main/src/ffc/hooks.ts
* @private
*/
export const useInitFfcEffect = (featureFlagClient: Ffc, option: IOption) => {
const [flags, setFlags] = useState<Record<string, IFeatureFlagSet>>({});
useEffect(() => {
featureFlagClient.init(option);
featureFlagClient.on('ff_update', (changes: unknown[]) => {
if (changes.length) {
setFlags(featureFlagClient.getAllFeatureFlags());
}
});
featureFlagClient.waitUntilReady().then((data: unknown[]) => {
if (data.length) {
setFlags(featureFlagClient.getAllFeatureFlags());
}
});
return () => {
// FIX destroy ffcClient
// ffcClient.logout();
};
}, [featureFlagClient, option, setFlags]);
return flags;
};
export const FeatureFlagsContext = createContext<{
ffcClient: Ffc;
flags: IFeatureFlagSet;
// TODO use genErrorObj
}>({} as any);
/**
* @example
* ```ts
* const App = () => (
* <FeatureFlagsProvider>
* <Page />
* </FeatureFlagsProvider>
* );
* ```
*/
export const FeatureFlagsProvider = ({ children }: { children: ReactNode }) => {
const flags = useInitFfcEffect(ffcClient, config);
return (
<FeatureFlagsContext.Provider value={{ ffcClient, flags }}>
{children}
</FeatureFlagsContext.Provider>
);
};
@@ -0,0 +1,11 @@
import type { IOption } from 'ffc-js-client-side-sdk/esm/types';
export const config: IOption = {
secret: process.env['AFFINE_FEATURE_FLAG_TOKEN'],
anonymous: true,
// user: {
// userName: 'the user's user name',
// id: 'the user's unique identifier'
// }
devModePassword: '-',
};
+111
View File
@@ -0,0 +1,111 @@
// Feature Flags
// Website https://featureflag.co/
// SDK doc https://github.com/feature-flags-co/ffc-js-client-side-sdk
// Demo https://github.com/feature-flags-co/ffc-js-client-side-sdk-react-jotai-demo
import type { IUser } from 'ffc-js-client-side-sdk/esm/types';
import { useContext } from 'react';
import { FeatureFlagsContext } from './Context';
/**
* The `ffcClient` should not export to external modules.
* Do not export this please.
*
* @private
*/
const useFfcClient = () => {
const { ffcClient } = useContext(FeatureFlagsContext);
return ffcClient;
};
/**
* NOTICE: The hook can no trigger the `Ffc.sendFeatureFlagInsight`,
* may lead to inaccurate telemetry data.
*
* Use it discreetly.
*/
const useFlags = () => {
const { flags } = useContext(FeatureFlagsContext);
return flags;
};
type UseFlagFn = (<T = unknown>(flag: string) => T | undefined) &
(<T = unknown>(flag: string, defaultValue: T) => T) &
// Workaround for infers boolean as true or false
// Remove this after the issue fixed
// See https://github.com/microsoft/TypeScript/issues/29400
((flag: string, defaultValue: false) => boolean) &
((flag: string, defaultValue: true) => boolean);
/**
* Returns the specified flag.
*
* The parameter defaultValue should have the same data type with flag type.
*
* @public
* @example
* ```ts
* const App = () => {
* const flag1 = useFlag('flag1', 'default value');
* // ^? string
* const flag2 = useFlag<boolean>('flag2');
* // ^? boolean | undefined
* return <div>flag1: {flag1}</div>
* }
* ```
*/
export const useFlag = ((flag: string, defaultValue: unknown) => {
const flags = useFlags();
const ffcClient = useFfcClient();
// @ts-expect-error This is a BUG for ffc-js-client. ffc-js-client supported typed flag now.
ffcClient.sendFeatureFlagInsight(flag, defaultValue);
const value: unknown = flags[flag];
if (defaultValue === undefined) {
// Can not guess the type, return the flag value directly
return value;
}
if (value === undefined) {
return defaultValue;
}
if (typeof value !== typeof defaultValue) {
console.error(
`[Feature Flags] Flag "${flag}" type mismatch! Make ensure you have set the correct type in feature flag portal! flag type: "${typeof value}", defaultValue type: "${typeof defaultValue}"`,
'flag:',
value,
'defaultValue:',
defaultValue
);
return defaultValue;
}
return value;
}) as UseFlagFn;
/**
* Set the user after initialization
*
* If the user parameter cannot be passed by the init method,
* the following method can be used to set the user after initialization.
*
* See https://github.com/feature-flags-co/ffc-js-client-side-sdk#set-the-user-after-initialization
*/
export const useIdentifyUser = () => {
const ffcClient = useFfcClient();
return (user: IUser) => ffcClient.identify(user);
};
/**
* Set the user to anonymous user
*
* We can manually call the method logout, which will switch the current user back to anonymous user if exists already or create a new anonymous user.
*
* See https://github.com/feature-flags-co/ffc-js-client-side-sdk#set-the-user-to-anonymous-user
*/
export const useLogout = () => {
const ffcClient = useFfcClient();
return () => ffcClient.logout();
};
export { FeatureFlagsProvider } from './Context';
@@ -0,0 +1,27 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"strictNullChecks": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"importsNotUsedAsValues": "error"
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
@@ -0,0 +1,23 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}
@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts"
]
}