@nfq/feature-flags
v3.2.0-next.15.0.0-canary.173.1
Published
Add feature flags to babel, nextjs, SWC, react and default js code
Downloads
529
Maintainers
Readme
@nfq/feature-flags
Table of Contents
- Description
- Installation
- Configuration
- Configuration Settings (Babel Plugin)
- JSX props
- Usage
- Contributions
- Licence
- Questions
Description:
This is an node module that aims for simple configuration and usage of feature flags. It is build for especially build time feature flags to enable treeshaking of unshipped features depending on the environment.
Installation:
To install the package run
npm install @nfq/feature-flags
if you are in yarn
yarn add @nfq/feature-flags
or on pnpm
pnpm install @nfq/feature-flags
Configuration:
To use feature flags there is some small configuration needed:
.eslintrc:
change it to an .eslintrc.js file because we need some functions. To prevent eslint from complaining about not existing modules there is an eslint function to set some rules for you.
The Function params used are the featureFlagImport option and the jsxImport option for the babel config to prevent eslint from firing on these pseudo imports.
example:
// can be used like this
const featureFlagsRules = require('@nfq/feature-flags/eslint/import')(['@app/features', '@nfq/feature-flags/jsx']);
// or this
const withFeatureFlags = require('@nfq/feature-flags/eslint/import');
const featureFlagsRules = withFeatureFlags(['@app/features', '@nfq/feature-flags/jsx']);
module.exports = {
extends: [
'@nfq'
],
rules: {
...featureFlagsRules.rules
}
};
next.config.js:
If you are in an nextjs environment you need some extra config to tell webpack not to cache your babel config. So here is also some little helper.
example:
const {withFeatureFlags} = require('@nfq/feature-flags/next');
module.exports = withFeatureFlags({
// nextjs config like normal.
});
next.config.js using it with SWC:
If you are in an nextjs environment and want to use it with SWC you need some extra config to tell SWC about the Plugin and also use selected environments and feature flags you defined in the features.*.js. All options from the babel config are applicable here too.
example:
const {withFeatureFlags} = require('@nfq/feature-flags/next');
const liveFeatures = require('./features.live');
const stageFeatures = require('./features.stage');
module.exports = withFeatureFlags({
// nextjs config like normal.
experimental: {
swcPlugins: [[
'@nfq/feature-flags', {
featureEnv: process.env.FEATURE_ENV || 'stage',
featureFlags: {
live: liveFeatures,
stage: stageFeatures
},
// optional settings. These are the defaults and can be omitted.
deprecationEnv: 'live',
featureFlagImport: '@app/features',
jsxImport: '@nfq/feature-flags/jsx',
jsxWithFeature: 'WithFeature',
jsxWithoutFeature: 'WithoutFeature'
}
]]
},
});
$${\color{red}Warning!!}$$ For the Plugin to work you have to make sure its the right Version in conjunction of the SWC core version used by your nextjs version. Here is a list for supported Versions so far:
| SWC Version | Plugin Version |
| ----------- | -------------- |
| 0.9*.*
| 3.*.*
|
webpack.config.js:
If you don't use nextjs but webpack use this instead. The param can be an Object or an function.
example:
const {withFeatureFlags} = require('@nfq/feature-flags/webpack');
module.exports = withFeatureFlags({
// webpack config like normal.
});
package.json
To tell webpack that your project is treeshakeble you need to set an flag for it. Webpack documentation for sideEffects
example:
{
...
"sideEffects": [
"**/*.css"
],
...
}
Also to tell the plugin and module on build time in which environment it is running you have to set an
FEATURE_ENV=
variable these define your possible environments.
example:
{
...
"scripts": {
"build:live": "cross-env FEATURE_ENV=live next build && cross-env FEATURE_ENV=live next export",
"build:stage": "cross-env FEATURE_ENV=stage next build && cross-env FEATURE_ENV=stage next export",
"dev": "cross-env FEATURE_ENV=dev next dev",
},
...
}
feature files:
Last but not least you need some feature files that define your flags for the environments.
Filenames look always the same with features.FEATURE_ENV.js
the FEATURE_ENV part is your defined environment.
These files HAVE to live in your project root directory!
.babelrc:
change it to an babel.config.js file because we need some functions.
example:
const {withFeatureFlags} = require('@nfq/feature-flags/babel');
module.exports = withFeatureFlags({
plugins: [...],
presets: [...]
}, {
deprecationEnv: 'live',
featureFlagImport: '@app/features',
jsxImport: '@nfq/feature-flags/jsx',
jsxWithFeature: 'WithFeature',
jsxWithoutFeature: 'WithoutFeature'
});
Configuration Settings (Babel Plugin)
| Option | type | Default | Description | | ----------------- | ------ | ---------------------- | ------------------------------------------------------------------------------- | | deprecationEnv | string | live | The feature env identifier that gets checked to show deprecations. | | featureFlagImport | string | @app/features | The import name to use if you want to import an feature flag | | jsxImport | string | @nfq/feature-flags/jsx | The import name to use if you want to import jsx helpers for react | | jsxWithFeature | string | WithFeature | The jsx component name to mark an subtree to only show with an specific feature | | jsxWithoutFeature | string | WithoutFeature | The jsx component name to mark an subtree to only show without an specific feature |
JSX props
You can import to JSX components both names defined in the babel settings section. Both have the exact same props you can use.
- The
jsxWithFeature
component will only render its subtree if the feature is set to true. - The
jsxWithoutFeature
component will only render its subtree if the feature is set to false.
| Prop | type | required | Description |
| --------------- | --------------------------------- | :----------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| feature | FeatureFlag or Array[FeatureFlag] | :heavy_check_mark: | Defines the feature the component should look up to determine if it should render or not. |
| deprecatesOn | TimeString (YYYY-MM-DD format) | | Define an date on which the babel module will throw deprecation messages for this feature. If none is set you will get deprecation warnings if an feature is configured as true on the deprecationEnv
environment. |
| neverDeprecates | boolean | | If set to true the feature will never throw deprecation warnings. |
Usage:
feature.live.js (live as example FEATURE_ENV):
module.exports = {COOL_FEATURE: false};
feature.stage.js (stage as example FEATURE_ENV):
module.exports = {COOL_FEATURE: true};
Plain js Examples:
import {COOL_FEATURE} from '@app/features';
const oldFunction = () => {
/* ... */
}
const newShinyFunction = () => {
/* ... */
}
const featureOrNot = () => {
if (COOL_FEATURE) {
newShinyFunction();
} else {
oldFunction();
}
}
import {COOL_FEATURE} from '@app/features';
const featureOrNot = () => {
/* ... */
(COOL_FEATURE) && console.log('OnlyNew')
/* ... */
}
React Examples:
import {Component} from 'react';
import {COOL_FEATURE} from '@app/features';
import {WithFeature, WithoutFeature} from '@nfq/feature-flags/jsx';
import Live from 'Components/Live'; // gets eliminated on stage environment because feature is true.
import Stage from 'Components/Stage'; // gets eliminated on Live environment because feature is true.
export default class Test extends Component {
render() {
return (
<div>
<WithFeature feature={COOL_FEATURE}>
<p>This is only rendered and in the final bundle if the feature is active</p>
<Stage />
</WithFeature>
<WithoutFeature feature={COOL_FEATURE}>
<p>This is only rendered and in the final bundle if the feature is inactive</p>
<Live />
</WithoutFeature>
</div>
)
}
}
deprecateOn:
import {Component} from 'react';
import {COOL_FEATURE} from '@app/features';
import {WithFeature} from '@nfq/feature-flags/jsx';
export default class Test extends Component {
render() {
return (
<div>
<WithFeature deprecatesOn="2021-07-14" feature={COOL_FEATURE}>
<p>Sends an deprecation warning on build time if date is greater then deprecateOn</p>
</WithFeature>
</div>
)
}
}
Contributions:
.NFQ | Christoph Kruppe
License:
The licence used is: MIT
Click on licence badge for licence details:
Questions:
If you have any furter questions please contact the following email address: email: [email protected]