Skip to content

Commit

Permalink
Create Plugin: Allow setting feature flags via cli args (#984)
Browse files Browse the repository at this point in the history
Co-authored-by: Levente Balogh <[email protected]>
  • Loading branch information
jackw and leventebalogh committed Jul 8, 2024
1 parent 0517626 commit 268ff00
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/create-plugin/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const MIGRATION_CONFIG = {

export const UDPATE_CONFIG = {
// Files that should be overriden between configuration version updates.
filesToOverride: ['.config/'],
filesToOverride: ['.config/', '.cprc.json'],
};

// prettier-ignore
Expand Down
15 changes: 15 additions & 0 deletions packages/create-plugin/src/utils/tests/utils.config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DEFAULT_FEATURE_FLAGS } from '../../constants.js';
const mocks = vi.hoisted(() => {
return {
commandName: 'generate',
argv: {},
};
});

Expand All @@ -25,6 +26,7 @@ describe('getConfig', () => {
describe('Command: Generate', () => {
beforeEach(() => {
mocks.commandName = 'generate';
mocks.argv = {};
});

it('should give back a default config', async () => {
Expand All @@ -35,11 +37,24 @@ describe('getConfig', () => {
features: DEFAULT_FEATURE_FLAGS,
});
});

it('should override default feature flags via cli args', async () => {
mocks.argv = {
'feature-flags': 'bundleGrafanaUI',
};
const config = getConfig(tmpDir);

expect(config).toEqual({
version: getVersion(),
features: { ...DEFAULT_FEATURE_FLAGS, bundleGrafanaUI: true },
});
});
});

describe('Command: Update', () => {
beforeEach(() => {
mocks.commandName = 'update';
mocks.argv = {};
});

it('should give back the correct config when there are no config files at all', async () => {
Expand Down
58 changes: 58 additions & 0 deletions packages/create-plugin/src/utils/tests/utils.helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { partitionArr } from '../utils.helpers.js';

describe('partitionArr', () => {
it('should partition an array of numbers based on a predicate', () => {
const arr = [1, 2, 3, 4, 5, 6];
const isEven = (num: number) => num % 2 === 0;
const result = partitionArr(arr, isEven);
expect(result).toEqual([
[2, 4, 6],
[1, 3, 5],
]);
});

it('should partition an array of strings based on a predicate', () => {
const arr = ['apple', 'banana', 'cherry', 'date'];
const startsWithA = (str: string) => str[0] === 'a';
const result = partitionArr(arr, startsWithA);
expect(result).toEqual([['apple'], ['banana', 'cherry', 'date']]);
});

it('should return two empty arrays if the input array is empty', () => {
const arr: number[] = [];
const isEven = (num: number) => num % 2 === 0;
const result = partitionArr(arr, isEven);
expect(result).toEqual([[], []]);
});

it('should place all items in the first partition if all match the predicate', () => {
const arr = [2, 4, 6, 8];
const isEven = (num: number) => num % 2 === 0;
const result = partitionArr(arr, isEven);
expect(result).toEqual([[2, 4, 6, 8], []]);
});

it('should place all items in the second partition if none match the predicate', () => {
const arr = [1, 3, 5, 7];
const isEven = (num: number) => num % 2 === 0;
const result = partitionArr(arr, isEven);
expect(result).toEqual([[], [1, 3, 5, 7]]);
});

it('should work with a complex object array and predicate', () => {
const arr = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 },
];
const isUnder30 = (person: { name: string; age: number }) => person.age < 30;
const result = partitionArr(arr, isUnder30);
expect(result).toEqual([
[{ name: 'Alice', age: 25 }],
[
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 },
],
]);
});
});
36 changes: 31 additions & 5 deletions packages/create-plugin/src/utils/utils.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import fs from 'node:fs';
import path from 'node:path';
import { getVersion } from './utils.version.js';
import { commandName } from './utils.cli.js';
import { argv, commandName } from './utils.cli.js';
import { DEFAULT_FEATURE_FLAGS } from '../constants.js';
import { printBox } from './utils.console.js';
import { partitionArr } from './utils.helpers.js';

export type FeatureFlags = {
bundleGrafanaUI?: boolean;
Expand All @@ -21,6 +23,9 @@ export type UserConfig = {
features: FeatureFlags;
};

// TODO: Create a config manager of sorts so we don't call getConfig multiple times rendering multiple warnings.
let hasShownConfigWarnings = false;

export function getConfig(workDir = process.cwd()): CreatePluginConfig {
const rootConfig = getRootConfig(workDir);
const userConfig = getUserConfig(workDir);
Expand Down Expand Up @@ -83,11 +88,32 @@ function readRCFileSync(path: string): CreatePluginConfig | undefined {
}
}

// This function creates feature flags based on the defaults for generate command else flags read from config.
// In all cases it will override the flags with the featureFlag cli arg values.
function createFeatureFlags(flags?: FeatureFlags): FeatureFlags {
// Default values for new scaffoldings
if (commandName === 'generate') {
return DEFAULT_FEATURE_FLAGS;
const featureFlags = commandName === 'generate' ? DEFAULT_FEATURE_FLAGS : flags ?? {};
const cliArgFlags = parseFeatureFlagsFromCliArgs();
return { ...featureFlags, ...cliArgFlags };
}

function parseFeatureFlagsFromCliArgs() {
const flagsfromCliArgs: string[] = argv['feature-flags'] ? argv['feature-flags'].split(',') : [];
const availableFeatureFlags = Object.keys(DEFAULT_FEATURE_FLAGS);
const [knownFlags, unknownFlags] = partitionArr(flagsfromCliArgs, (item) => availableFeatureFlags.includes(item));

if (unknownFlags.length > 0 && !hasShownConfigWarnings) {
printBox({
title: 'Warning! Unknown feature flags detected.',
subtitle: ``,
content: `The following feature-flags are unknown: ${unknownFlags.join(
', '
)}.\n\nAvailable feature-flags are: ${availableFeatureFlags.join(', ')}`,
color: 'yellow',
});
hasShownConfigWarnings = true;
}

return flags ?? {};
return knownFlags.reduce((acc, flag) => {
return { ...acc, [flag]: true };
}, {} as FeatureFlags);
}
3 changes: 3 additions & 0 deletions packages/create-plugin/src/utils/utils.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function partitionArr<T>(arr: T[], pred: (item: T) => boolean): [T[], T[]] {
return arr.reduce<[T[], T[]]>((acc, i) => (acc[pred(i) ? 0 : 1].push(i), acc), [[], []]);
}
6 changes: 2 additions & 4 deletions packages/create-plugin/templates/common/.cprc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"features": {
{{#if bundleGrafanaUI}}
"bundleGrafanaUI": true{{/if}}
{{#if useReactRouterV6}}
"useReactRouterV6": true{{/if}}
"bundleGrafanaUI": {{ bundleGrafanaUI }},
"useReactRouterV6": {{ useReactRouterV6 }}
}
}

0 comments on commit 268ff00

Please sign in to comment.