diff --git a/packages/create-plugin/src/constants.ts b/packages/create-plugin/src/constants.ts index 42c416f78..d4bd1dd53 100644 --- a/packages/create-plugin/src/constants.ts +++ b/packages/create-plugin/src/constants.ts @@ -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 diff --git a/packages/create-plugin/src/utils/tests/utils.config.test.ts b/packages/create-plugin/src/utils/tests/utils.config.test.ts index 01125a4ab..b7989169a 100644 --- a/packages/create-plugin/src/utils/tests/utils.config.test.ts +++ b/packages/create-plugin/src/utils/tests/utils.config.test.ts @@ -9,6 +9,7 @@ import { DEFAULT_FEATURE_FLAGS } from '../../constants.js'; const mocks = vi.hoisted(() => { return { commandName: 'generate', + argv: {}, }; }); @@ -25,6 +26,7 @@ describe('getConfig', () => { describe('Command: Generate', () => { beforeEach(() => { mocks.commandName = 'generate'; + mocks.argv = {}; }); it('should give back a default config', async () => { @@ -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 () => { diff --git a/packages/create-plugin/src/utils/tests/utils.helpers.test.ts b/packages/create-plugin/src/utils/tests/utils.helpers.test.ts new file mode 100644 index 000000000..26fee5b6c --- /dev/null +++ b/packages/create-plugin/src/utils/tests/utils.helpers.test.ts @@ -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 }, + ], + ]); + }); +}); diff --git a/packages/create-plugin/src/utils/utils.config.ts b/packages/create-plugin/src/utils/utils.config.ts index ec08d2e9d..4954e103a 100644 --- a/packages/create-plugin/src/utils/utils.config.ts +++ b/packages/create-plugin/src/utils/utils.config.ts @@ -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; @@ -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); @@ -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); } diff --git a/packages/create-plugin/src/utils/utils.helpers.ts b/packages/create-plugin/src/utils/utils.helpers.ts new file mode 100644 index 000000000..66a3af069 --- /dev/null +++ b/packages/create-plugin/src/utils/utils.helpers.ts @@ -0,0 +1,3 @@ +export function partitionArr(arr: T[], pred: (item: T) => boolean): [T[], T[]] { + return arr.reduce<[T[], T[]]>((acc, i) => (acc[pred(i) ? 0 : 1].push(i), acc), [[], []]); +} diff --git a/packages/create-plugin/templates/common/.cprc.json b/packages/create-plugin/templates/common/.cprc.json index 62fed26eb..bc6ba9919 100644 --- a/packages/create-plugin/templates/common/.cprc.json +++ b/packages/create-plugin/templates/common/.cprc.json @@ -1,8 +1,6 @@ { "features": { - {{#if bundleGrafanaUI}} - "bundleGrafanaUI": true{{/if}} - {{#if useReactRouterV6}} - "useReactRouterV6": true{{/if}} + "bundleGrafanaUI": {{ bundleGrafanaUI }}, + "useReactRouterV6": {{ useReactRouterV6 }} } }