@squide/firefly-rsbuild-storybook 1.0.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @squide/firefly-rsbuild-storybook
2
2
 
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#392](https://github.com/workleap/wl-squide/pull/392) [`4001ae7`](https://github.com/workleap/wl-squide/commit/4001ae75f7aea8ee124ce831f69a1f55a342cef5) Thanks [@patricklafrance](https://github.com/patricklafrance)! - `initializeFireflyForStorybook` now accept a `useMsw` option to opt-out of MSW and will now render as expected if no local modules are provided.
8
+
9
+ - Updated dependencies [[`4001ae7`](https://github.com/workleap/wl-squide/commit/4001ae75f7aea8ee124ce831f69a1f55a342cef5)]:
10
+ - @squide/firefly@16.1.7
11
+ - @squide/env-vars@1.4.15
12
+ - @squide/launch-darkly@1.0.6
13
+ - @squide/msw@4.0.13
14
+
15
+ ## 2.0.0
16
+
17
+ ### Major Changes
18
+
19
+ - [#373](https://github.com/workleap/wl-squide/pull/373) [`f9138cc`](https://github.com/workleap/wl-squide/commit/f9138cc4bd0a1d175ad4bd7e695d1edd7fbe4c1c) Thanks [@alexasselin008](https://github.com/alexasselin008)! - - `withFeatureFlagsOverrideDecorator` now accept a single `overrides` argument. The feature flags are now retrieved directly from the runtime.
20
+ - Bumped dependencies.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [[`f9138cc`](https://github.com/workleap/wl-squide/commit/f9138cc4bd0a1d175ad4bd7e695d1edd7fbe4c1c), [`f9138cc`](https://github.com/workleap/wl-squide/commit/f9138cc4bd0a1d175ad4bd7e695d1edd7fbe4c1c)]:
25
+ - @squide/launch-darkly@1.0.5
26
+ - @squide/env-vars@1.4.14
27
+ - @squide/firefly@16.1.6
28
+ - @squide/msw@4.0.12
29
+
3
30
  ## 1.0.5
4
31
 
5
32
  ### Patch Changes
@@ -1,13 +1,15 @@
1
1
  import { EnvironmentVariables } from "@squide/env-vars";
2
2
  import { FireflyRuntime, ModuleRegisterFunction } from "@squide/firefly";
3
+ import { FeatureFlags } from "@squide/launch-darkly";
3
4
  import { RootLogger } from "@workleap/logging";
4
- import { LDClient, LDFlagValue } from "launchdarkly-js-client-sdk";
5
+ import { LDClient } from "launchdarkly-js-client-sdk";
5
6
  import { StorybookRuntime } from "./StorybookRuntime.ts";
6
7
  export interface InitializeFireflyForStorybookOptions {
7
8
  localModules?: ModuleRegisterFunction<FireflyRuntime>[];
8
9
  environmentVariables?: EnvironmentVariables;
9
- featureFlags?: Map<string, LDFlagValue> | Record<string, LDFlagValue>;
10
+ featureFlags?: Partial<FeatureFlags>;
10
11
  launchDarklyClient?: LDClient;
11
12
  loggers?: RootLogger[];
13
+ useMsw?: boolean;
12
14
  }
13
15
  export declare function initializeFireflyForStorybook(options?: InitializeFireflyForStorybookOptions): Promise<StorybookRuntime>;
@@ -17,14 +17,14 @@ import { StorybookRuntime } from "./StorybookRuntime.js";
17
17
 
18
18
 
19
19
  function logInitializationState(runtime, options, plugins) {
20
- const { localModules, environmentVariables, featureFlags, launchDarklyClient } = options;
20
+ const { localModules, environmentVariables, featureFlags, launchDarklyClient, useMsw } = options;
21
21
  const scope = runtime.logger.startScope("[squide] Initializing the application.");
22
22
  try {
23
23
  scope.information("[squide] Mode: development");
24
24
  if (localModules) {
25
25
  scope.withText("[squide] Local modules:").withObject(localModules).information();
26
26
  }
27
- scope.information("[squide] Use MSW: Yes");
27
+ scope.information(`[squide] Use MSW: ${useMsw ? "Yes" : "No"}`);
28
28
  if (environmentVariables && Object.keys(environmentVariables).length > 0) {
29
29
  scope.withText("[squide] Environment variables:").withObject(environmentVariables).information();
30
30
  }
@@ -42,27 +42,32 @@ function logInitializationState(runtime, options, plugins) {
42
42
  }
43
43
  }
44
44
  async function initializeFireflyForStorybook(options = {}) {
45
- const { localModules, environmentVariables, featureFlags = {}, launchDarklyClient, loggers } = options;
45
+ const { localModules = [], environmentVariables, featureFlags = {}, launchDarklyClient, loggers, useMsw = true } = options;
46
46
  const plugins = [
47
- (x)=>new MswPlugin(x),
48
47
  (x)=>new EnvironmentVariablesPlugin(x, {
49
48
  variables: environmentVariables
50
49
  }),
51
- (x)=>new LaunchDarklyPlugin(x, // 1- If no client is provided create it.
52
- // 2- If feature flags are provided and it's an instance of Map, use the argument.
53
- // 3- If feature flags are provided (or the default value) and it's an object literal, convert the object to a Map instance.
54
- launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags instanceof Map ? featureFlags : new Map(Object.entries(featureFlags))))
50
+ (x)=>new LaunchDarklyPlugin(x, launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags))
55
51
  ];
52
+ if (useMsw) {
53
+ plugins.push((x)=>new MswPlugin(x));
54
+ }
56
55
  const runtime = new StorybookRuntime({
57
56
  mode: "development",
58
57
  plugins,
59
58
  loggers
60
59
  });
61
60
  logInitializationState(runtime, options, plugins);
62
- if (localModules && localModules.length > 0) {
61
+ if (localModules.length > 0) {
63
62
  await runtime.moduleManager.registerModules([
64
63
  ...toLocalModuleDefinitions(localModules)
65
64
  ]);
65
+ } else {
66
+ // There's a possibility that either no local modules are provided.
67
+ // This use case is hard to catch at the module manager level because it could also
68
+ // mean that the registration process hasn't started yet.
69
+ // This is safer to handle it here because we got all the modules functions.
70
+ runtime.moduleManager.setAsReady();
66
71
  }
67
72
  return runtime;
68
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"initializeFireflyForStorybook.js","sources":["../src/initializeFireflyForStorybook.ts"],"sourcesContent":["import { EnvironmentVariables, EnvironmentVariablesPlugin } from \"@squide/env-vars\";\nimport { FireflyRuntime, InMemoryLaunchDarklyClient, LaunchDarklyPlugin, ModuleRegisterFunction, PluginFactory, toLocalModuleDefinitions } from \"@squide/firefly\";\nimport { MswPlugin } from \"@squide/msw\";\nimport { RootLogger } from \"@workleap/logging\";\nimport { LDClient, LDFlagValue } from \"launchdarkly-js-client-sdk\";\nimport { StorybookRuntime } from \"./StorybookRuntime.ts\";\n\nexport interface InitializeFireflyForStorybookOptions {\n localModules?: ModuleRegisterFunction<FireflyRuntime>[];\n environmentVariables?: EnvironmentVariables;\n featureFlags?: Map<string, LDFlagValue> | Record<string, LDFlagValue>;\n launchDarklyClient?: LDClient;\n loggers?: RootLogger[];\n}\n\nfunction logInitializationState(\n runtime: FireflyRuntime,\n options: InitializeFireflyForStorybookOptions,\n plugins: PluginFactory<FireflyRuntime>[]\n) {\n const {\n localModules,\n environmentVariables,\n featureFlags,\n launchDarklyClient\n } = options;\n const scope = (runtime.logger as RootLogger).startScope(\"[squide] Initializing the application.\");\n\n try {\n scope.information(\"[squide] Mode: development\");\n\n if (localModules) {\n scope\n .withText(\"[squide] Local modules:\")\n .withObject(localModules)\n .information();\n }\n\n scope.information(\"[squide] Use MSW: Yes\");\n\n if (environmentVariables && Object.keys(environmentVariables).length > 0) {\n scope\n .withText(\"[squide] Environment variables:\")\n .withObject(environmentVariables)\n .information();\n }\n\n if (featureFlags) {\n scope\n .withText(\"[squide] Feature flags:\")\n .withObject(featureFlags)\n .information();\n }\n\n if (launchDarklyClient) {\n scope\n .withText(\"[squide] LaunchDarkly client:\")\n .withObject(launchDarklyClient)\n .information();\n }\n\n if (plugins.length > 0) {\n scope\n .withText(\"[squide] Plugins:\")\n .withObject(plugins)\n .information();\n }\n } finally {\n scope.end();\n }\n}\n\nexport async function initializeFireflyForStorybook(options: InitializeFireflyForStorybookOptions = {}) {\n const {\n localModules,\n environmentVariables,\n featureFlags = {},\n launchDarklyClient,\n loggers\n } = options;\n\n const plugins: PluginFactory<FireflyRuntime>[] = [\n x => new MswPlugin(x),\n x => new EnvironmentVariablesPlugin(x, {\n variables: environmentVariables\n }),\n x => new LaunchDarklyPlugin(\n x,\n // 1- If no client is provided create it.\n // 2- If feature flags are provided and it's an instance of Map, use the argument.\n // 3- If feature flags are provided (or the default value) and it's an object literal, convert the object to a Map instance.\n launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags instanceof Map\n ? featureFlags\n : new Map<string, LDFlagValue>(Object.entries(featureFlags))\n )\n )\n ];\n\n const runtime = new StorybookRuntime({\n mode: \"development\",\n plugins,\n loggers\n });\n\n logInitializationState(runtime, options, plugins);\n\n if (localModules && localModules.length > 0) {\n await runtime.moduleManager.registerModules([\n ...toLocalModuleDefinitions(localModules)\n ]);\n }\n\n return runtime;\n}\n"],"names":["EnvironmentVariablesPlugin","InMemoryLaunchDarklyClient","LaunchDarklyPlugin","toLocalModuleDefinitions","MswPlugin","StorybookRuntime","logInitializationState","runtime","options","plugins","localModules","environmentVariables","featureFlags","launchDarklyClient","scope","Object","initializeFireflyForStorybook","loggers","x","Map"],"mappings":";;;;;;;;;;;;;;AAAoF;AAC8E;AAC1H;AAGiB;AAUzD,SAASM,uBACLC,OAAuB,EACvBC,OAA6C,EAC7CC,OAAwC;IAExC,MAAM,EACFC,YAAY,EACZC,oBAAoB,EACpBC,YAAY,EACZC,kBAAkB,EACrB,GAAGL;IACJ,MAAMM,QAASP,QAAQ,MAAM,CAAgB,UAAU,CAAC;IAExD,IAAI;QACAO,MAAM,WAAW,CAAC;QAElB,IAAIJ,cAAc;YACdI,MACK,QAAQ,CAAC,2BACT,UAAU,CAACJ,cACX,WAAW;QACpB;QAEAI,MAAM,WAAW,CAAC;QAElB,IAAIH,wBAAwBI,OAAO,IAAI,CAACJ,sBAAsB,MAAM,GAAG,GAAG;YACtEG,MACK,QAAQ,CAAC,mCACT,UAAU,CAACH,sBACX,WAAW;QACpB;QAEA,IAAIC,cAAc;YACdE,MACK,QAAQ,CAAC,2BACT,UAAU,CAACF,cACX,WAAW;QACpB;QAEA,IAAIC,oBAAoB;YACpBC,MACK,QAAQ,CAAC,iCACT,UAAU,CAACD,oBACX,WAAW;QACpB;QAEA,IAAIJ,QAAQ,MAAM,GAAG,GAAG;YACpBK,MACK,QAAQ,CAAC,qBACT,UAAU,CAACL,SACX,WAAW;QACpB;IACJ,SAAU;QACNK,MAAM,GAAG;IACb;AACJ;AAEO,eAAeE,8BAA8BR,UAAgD,CAAC,CAAC;IAClG,MAAM,EACFE,YAAY,EACZC,oBAAoB,EACpBC,eAAe,CAAC,CAAC,EACjBC,kBAAkB,EAClBI,OAAO,EACV,GAAGT;IAEJ,MAAMC,UAA2C;QAC7CS,CAAAA,IAAK,IAAId,SAASA,CAACc;QACnBA,CAAAA,IAAK,IAAIlB,0BAA0BA,CAACkB,GAAG;gBACnC,WAAWP;YACf;QACAO,CAAAA,IAAK,IAAIhB,kBAAkBA,CACvBgB,GACA,yCAAyC;YACzC,kFAAkF;YAClF,4HAA4H;YAC5HL,sBAAsB,IAAIZ,0BAA0BA,CAACW,wBAAwBO,MACvEP,eACA,IAAIO,IAAyBJ,OAAO,OAAO,CAACH;KAGzD;IAED,MAAML,UAAU,IAAIF,gBAAgBA,CAAC;QACjC,MAAM;QACNI;QACAQ;IACJ;IAEAX,uBAAuBC,SAASC,SAASC;IAEzC,IAAIC,gBAAgBA,aAAa,MAAM,GAAG,GAAG;QACzC,MAAMH,QAAQ,aAAa,CAAC,eAAe,CAAC;eACrCJ,wBAAwBA,CAACO;SAC/B;IACL;IAEA,OAAOH;AACX"}
1
+ {"version":3,"file":"initializeFireflyForStorybook.js","sources":["../src/initializeFireflyForStorybook.ts"],"sourcesContent":["import { EnvironmentVariables, EnvironmentVariablesPlugin } from \"@squide/env-vars\";\nimport { FireflyRuntime, InMemoryLaunchDarklyClient, LaunchDarklyPlugin, ModuleRegisterFunction, PluginFactory, toLocalModuleDefinitions } from \"@squide/firefly\";\nimport { FeatureFlags } from \"@squide/launch-darkly\";\nimport { MswPlugin } from \"@squide/msw\";\nimport { RootLogger } from \"@workleap/logging\";\nimport { LDClient } from \"launchdarkly-js-client-sdk\";\nimport { StorybookRuntime } from \"./StorybookRuntime.ts\";\n\nexport interface InitializeFireflyForStorybookOptions {\n localModules?: ModuleRegisterFunction<FireflyRuntime>[];\n environmentVariables?: EnvironmentVariables;\n featureFlags?: Partial<FeatureFlags>;\n launchDarklyClient?: LDClient;\n loggers?: RootLogger[];\n useMsw?: boolean;\n}\n\nfunction logInitializationState(\n runtime: FireflyRuntime,\n options: InitializeFireflyForStorybookOptions,\n plugins: PluginFactory<FireflyRuntime>[]\n) {\n const {\n localModules,\n environmentVariables,\n featureFlags,\n launchDarklyClient,\n useMsw\n } = options;\n const scope = (runtime.logger as RootLogger).startScope(\"[squide] Initializing the application.\");\n\n try {\n scope.information(\"[squide] Mode: development\");\n\n if (localModules) {\n scope\n .withText(\"[squide] Local modules:\")\n .withObject(localModules)\n .information();\n }\n\n scope.information(`[squide] Use MSW: ${useMsw ? \"Yes\" : \"No\"}`);\n\n if (environmentVariables && Object.keys(environmentVariables).length > 0) {\n scope\n .withText(\"[squide] Environment variables:\")\n .withObject(environmentVariables)\n .information();\n }\n\n if (featureFlags) {\n scope\n .withText(\"[squide] Feature flags:\")\n .withObject(featureFlags)\n .information();\n }\n\n if (launchDarklyClient) {\n scope\n .withText(\"[squide] LaunchDarkly client:\")\n .withObject(launchDarklyClient)\n .information();\n }\n\n if (plugins.length > 0) {\n scope\n .withText(\"[squide] Plugins:\")\n .withObject(plugins)\n .information();\n }\n } finally {\n scope.end();\n }\n}\n\nexport async function initializeFireflyForStorybook(options: InitializeFireflyForStorybookOptions = {}) {\n const {\n localModules = [],\n environmentVariables,\n featureFlags = {},\n launchDarklyClient,\n loggers,\n useMsw = true\n } = options;\n\n const plugins: PluginFactory<FireflyRuntime>[] = [\n x => new EnvironmentVariablesPlugin(x, {\n variables: environmentVariables\n }),\n x => new LaunchDarklyPlugin(x, launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags))\n ];\n\n if (useMsw) {\n plugins.push(x => new MswPlugin(x));\n }\n\n const runtime = new StorybookRuntime({\n mode: \"development\",\n plugins,\n loggers\n });\n\n logInitializationState(runtime, options, plugins);\n\n if (localModules.length > 0) {\n await runtime.moduleManager.registerModules([\n ...toLocalModuleDefinitions(localModules)\n ]);\n } else {\n // There's a possibility that either no local modules are provided.\n // This use case is hard to catch at the module manager level because it could also\n // mean that the registration process hasn't started yet.\n // This is safer to handle it here because we got all the modules functions.\n runtime.moduleManager.setAsReady();\n }\n\n return runtime;\n}\n"],"names":["EnvironmentVariablesPlugin","InMemoryLaunchDarklyClient","LaunchDarklyPlugin","toLocalModuleDefinitions","MswPlugin","StorybookRuntime","logInitializationState","runtime","options","plugins","localModules","environmentVariables","featureFlags","launchDarklyClient","useMsw","scope","Object","initializeFireflyForStorybook","loggers","x"],"mappings":";;;;;;;;;;;;;;AAAoF;AAC8E;AAE1H;AAGiB;AAWzD,SAASM,uBACLC,OAAuB,EACvBC,OAA6C,EAC7CC,OAAwC;IAExC,MAAM,EACFC,YAAY,EACZC,oBAAoB,EACpBC,YAAY,EACZC,kBAAkB,EAClBC,MAAM,EACT,GAAGN;IACJ,MAAMO,QAASR,QAAQ,MAAM,CAAgB,UAAU,CAAC;IAExD,IAAI;QACAQ,MAAM,WAAW,CAAC;QAElB,IAAIL,cAAc;YACdK,MACK,QAAQ,CAAC,2BACT,UAAU,CAACL,cACX,WAAW;QACpB;QAEAK,MAAM,WAAW,CAAC,CAAC,kBAAkB,EAAED,SAAS,QAAQ,MAAM;QAE9D,IAAIH,wBAAwBK,OAAO,IAAI,CAACL,sBAAsB,MAAM,GAAG,GAAG;YACtEI,MACK,QAAQ,CAAC,mCACT,UAAU,CAACJ,sBACX,WAAW;QACpB;QAEA,IAAIC,cAAc;YACdG,MACK,QAAQ,CAAC,2BACT,UAAU,CAACH,cACX,WAAW;QACpB;QAEA,IAAIC,oBAAoB;YACpBE,MACK,QAAQ,CAAC,iCACT,UAAU,CAACF,oBACX,WAAW;QACpB;QAEA,IAAIJ,QAAQ,MAAM,GAAG,GAAG;YACpBM,MACK,QAAQ,CAAC,qBACT,UAAU,CAACN,SACX,WAAW;QACpB;IACJ,SAAU;QACNM,MAAM,GAAG;IACb;AACJ;AAEO,eAAeE,8BAA8BT,UAAgD,CAAC,CAAC;IAClG,MAAM,EACFE,eAAe,EAAE,EACjBC,oBAAoB,EACpBC,eAAe,CAAC,CAAC,EACjBC,kBAAkB,EAClBK,OAAO,EACPJ,SAAS,IAAI,EAChB,GAAGN;IAEJ,MAAMC,UAA2C;QAC7CU,CAAAA,IAAK,IAAInB,0BAA0BA,CAACmB,GAAG;gBACnC,WAAWR;YACf;QACAQ,CAAAA,IAAK,IAAIjB,kBAAkBA,CAACiB,GAAGN,sBAAsB,IAAIZ,0BAA0BA,CAACW;KACvF;IAED,IAAIE,QAAQ;QACRL,QAAQ,IAAI,CAACU,CAAAA,IAAK,IAAIf,SAASA,CAACe;IACpC;IAEA,MAAMZ,UAAU,IAAIF,gBAAgBA,CAAC;QACjC,MAAM;QACNI;QACAS;IACJ;IAEAZ,uBAAuBC,SAASC,SAASC;IAEzC,IAAIC,aAAa,MAAM,GAAG,GAAG;QACzB,MAAMH,QAAQ,aAAa,CAAC,eAAe,CAAC;eACrCJ,wBAAwBA,CAACO;SAC/B;IACL,OAAO;QACH,mEAAmE;QACnE,mFAAmF;QACnF,yDAAyD;QACzD,4EAA4E;QAC5EH,QAAQ,aAAa,CAAC,UAAU;IACpC;IAEA,OAAOA;AACX"}
@@ -1,3 +1,3 @@
1
- import { LDFlagValue } from "launchdarkly-js-client-sdk";
1
+ import { FeatureFlags } from "@squide/launch-darkly";
2
2
  import { Decorator } from "storybook-react-rsbuild";
3
- export declare function withFeatureFlagsOverrideDecorator<const T extends string>(featureFlags: Map<T, LDFlagValue>, overrides: Partial<Record<T, LDFlagValue>>): Decorator;
3
+ export declare function withFeatureFlagsOverrideDecorator(overrides: Partial<FeatureFlags>): Decorator;
@@ -1,41 +1,45 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { useLayoutEffect } from "react";
2
+ import { isEditableLaunchDarklyClient, useLaunchDarklyClient } from "@squide/launch-darkly";
3
+ import { useEffect, useRef } from "react";
3
4
 
4
5
  ;// CONCATENATED MODULE: external "react/jsx-runtime"
5
6
 
7
+ ;// CONCATENATED MODULE: external "@squide/launch-darkly"
8
+
6
9
  ;// CONCATENATED MODULE: external "react"
7
10
 
8
11
  ;// CONCATENATED MODULE: ./src/withFeatureFlagsOverrideDecorator.tsx
9
12
 
10
13
 
14
+
15
+ // Used to override the flags in a "useEffect" hook but then the unit test were not working.
16
+ // To fix the unit tests, the flags are now overrided directly in the render function.
11
17
  function OverrideFeatureFlags(props) {
12
- const { featureFlags, overrides, children } = props;
13
- // Must use a layout effect to override the feature flags before the story renders.
14
- useLayoutEffect(()=>{
15
- const originalValues = {};
16
- Object.entries(overrides).forEach(([key, value])=>{
17
- originalValues[key] = featureFlags.get(key);
18
- featureFlags.set(key, value);
19
- });
18
+ const { overrides, children } = props;
19
+ const transactionRef = useRef(undefined);
20
+ const client = useLaunchDarklyClient();
21
+ // eslint-disable-next-line react-hooks/refs
22
+ if (!transactionRef.current) {
23
+ if (!isEditableLaunchDarklyClient(client)) {
24
+ throw new Error("[squide] The withFeatureFlagsOverrideDecorator hook can only be used with an EditableLaunchDarklyClient instance.");
25
+ }
26
+ transactionRef.current = client.startTransaction();
27
+ client.setFeatureFlags(overrides);
28
+ }
29
+ useEffect(()=>{
20
30
  return ()=>{
21
31
  // Reset the feature flags to the original values.
22
- Object.entries(originalValues).forEach(([key, value])=>{
23
- featureFlags.set(key, value);
24
- });
32
+ transactionRef.current?.undo();
33
+ transactionRef.current = undefined;
25
34
  };
26
35
  }, [
27
- featureFlags,
28
- overrides
36
+ transactionRef
29
37
  ]);
30
38
  return children;
31
39
  }
32
- function withFeatureFlagsOverrideDecorator(featureFlags, overrides) {
33
- if (!(featureFlags instanceof Map)) {
34
- throw new TypeError("[squide] The \"featureFlags\" argument must be a Map instance.");
35
- }
40
+ function withFeatureFlagsOverrideDecorator(overrides) {
36
41
  return (story)=>{
37
42
  return /*#__PURE__*/ jsx(OverrideFeatureFlags, {
38
- featureFlags: featureFlags,
39
43
  overrides: overrides,
40
44
  children: story()
41
45
  });
@@ -1 +1 @@
1
- {"version":3,"file":"withFeatureFlagsOverrideDecorator.js","sources":["../src/withFeatureFlagsOverrideDecorator.tsx"],"sourcesContent":["import { LDFlagValue } from \"launchdarkly-js-client-sdk\";\nimport { PropsWithChildren, useLayoutEffect } from \"react\";\nimport { Decorator } from \"storybook-react-rsbuild\";\n\ninterface OverrideFeatureFlagsProps extends PropsWithChildren {\n featureFlags: Map<string, LDFlagValue>;\n overrides: Record<string, LDFlagValue>;\n}\n\nfunction OverrideFeatureFlags(props: OverrideFeatureFlagsProps) {\n const {\n featureFlags,\n overrides,\n children\n } = props;\n\n // Must use a layout effect to override the feature flags before the story renders.\n useLayoutEffect(() => {\n const originalValues: Record<string, LDFlagValue> = {};\n\n Object.entries(overrides).forEach(([key, value]) => {\n originalValues[key] = featureFlags.get(key);\n featureFlags.set(key, value);\n });\n\n return () => {\n // Reset the feature flags to the original values.\n Object.entries(originalValues).forEach(([key, value]) => {\n featureFlags.set(key, value);\n });\n };\n }, [featureFlags, overrides]);\n\n return children;\n}\n\nexport function withFeatureFlagsOverrideDecorator<const T extends string>(featureFlags: Map<T, LDFlagValue>, overrides: Partial<Record<T, LDFlagValue>>): Decorator {\n if (!(featureFlags instanceof Map)) {\n throw new TypeError(\"[squide] The \\\"featureFlags\\\" argument must be a Map instance.\");\n }\n\n return story => {\n return (\n <OverrideFeatureFlags featureFlags={featureFlags} overrides={overrides}>\n {story()}\n </OverrideFeatureFlags>\n );\n };\n}\n"],"names":["useLayoutEffect","OverrideFeatureFlags","props","featureFlags","overrides","children","originalValues","Object","key","value","withFeatureFlagsOverrideDecorator","Map","TypeError","story"],"mappings":";;;;;;;;;AAC2D;AAQ3D,SAASC,qBAAqBC,KAAgC;IAC1D,MAAM,EACFC,YAAY,EACZC,SAAS,EACTC,QAAQ,EACX,GAAGH;IAEJ,mFAAmF;IACnFF,eAAeA,CAAC;QACZ,MAAMM,iBAA8C,CAAC;QAErDC,OAAO,OAAO,CAACH,WAAW,OAAO,CAAC,CAAC,CAACI,KAAKC,MAAM;YAC3CH,cAAc,CAACE,IAAI,GAAGL,aAAa,GAAG,CAACK;YACvCL,aAAa,GAAG,CAACK,KAAKC;QAC1B;QAEA,OAAO;YACH,kDAAkD;YAClDF,OAAO,OAAO,CAACD,gBAAgB,OAAO,CAAC,CAAC,CAACE,KAAKC,MAAM;gBAChDN,aAAa,GAAG,CAACK,KAAKC;YAC1B;QACJ;IACJ,GAAG;QAACN;QAAcC;KAAU;IAE5B,OAAOC;AACX;AAEO,SAASK,kCAA0DP,YAAiC,EAAEC,SAA0C;IACnJ,IAAI,CAAED,CAAAA,wBAAwBQ,GAAE,GAAI;QAChC,MAAM,IAAIC,UAAU;IACxB;IAEA,OAAOC,CAAAA;QACH,qBACI,IAACZ;YAAqB,cAAcE;YAAc,WAAWC;sBACxDS;;IAGb;AACJ"}
1
+ {"version":3,"file":"withFeatureFlagsOverrideDecorator.js","sources":["../src/withFeatureFlagsOverrideDecorator.tsx"],"sourcesContent":["import { FeatureFlags, isEditableLaunchDarklyClient, LaunchDarklyClientTransaction, useLaunchDarklyClient } from \"@squide/launch-darkly\";\nimport { PropsWithChildren, useEffect, useRef } from \"react\";\nimport { Decorator } from \"storybook-react-rsbuild\";\n\ninterface OverrideFeatureFlagsProps extends PropsWithChildren {\n overrides: Partial<FeatureFlags>;\n}\n\n// Used to override the flags in a \"useEffect\" hook but then the unit test were not working.\n// To fix the unit tests, the flags are now overrided directly in the render function.\nfunction OverrideFeatureFlags(props: OverrideFeatureFlagsProps) {\n const {\n overrides,\n children\n } = props;\n\n const transactionRef = useRef<LaunchDarklyClientTransaction | undefined>(undefined);\n const client = useLaunchDarklyClient();\n\n // eslint-disable-next-line react-hooks/refs\n if (!transactionRef.current) {\n if (!isEditableLaunchDarklyClient(client)) {\n throw new Error(\"[squide] The withFeatureFlagsOverrideDecorator hook can only be used with an EditableLaunchDarklyClient instance.\");\n }\n\n transactionRef.current = client.startTransaction();\n client.setFeatureFlags(overrides);\n }\n\n useEffect(() => {\n return () => {\n // Reset the feature flags to the original values.\n transactionRef.current?.undo();\n transactionRef.current = undefined;\n };\n }, [transactionRef]);\n\n return children;\n}\n\nexport function withFeatureFlagsOverrideDecorator(overrides: Partial<FeatureFlags>): Decorator {\n return story => {\n return (\n <OverrideFeatureFlags overrides={overrides}>\n {story()}\n </OverrideFeatureFlags>\n );\n };\n}\n"],"names":["isEditableLaunchDarklyClient","useLaunchDarklyClient","useEffect","useRef","OverrideFeatureFlags","props","overrides","children","transactionRef","undefined","client","Error","withFeatureFlagsOverrideDecorator","story"],"mappings":";;;;;;;;;;;;AAAyI;AAC5E;AAO7D,4FAA4F;AAC5F,sFAAsF;AACtF,SAASI,qBAAqBC,KAAgC;IAC1D,MAAM,EACFC,SAAS,EACTC,QAAQ,EACX,GAAGF;IAEJ,MAAMG,iBAAiBL,MAAMA,CAA4CM;IACzE,MAAMC,SAAST,qBAAqBA;IAEpC,4CAA4C;IAC5C,IAAI,CAACO,eAAe,OAAO,EAAE;QACzB,IAAI,CAACR,4BAA4BA,CAACU,SAAS;YACvC,MAAM,IAAIC,MAAM;QACpB;QAEAH,eAAe,OAAO,GAAGE,OAAO,gBAAgB;QAChDA,OAAO,eAAe,CAACJ;IAC3B;IAEAJ,SAASA,CAAC;QACN,OAAO;YACH,kDAAkD;YAClDM,eAAe,OAAO,EAAE;YACxBA,eAAe,OAAO,GAAGC;QAC7B;IACJ,GAAG;QAACD;KAAe;IAEnB,OAAOD;AACX;AAEO,SAASK,kCAAkCN,SAAgC;IAC9E,OAAOO,CAAAA;QACH,qBACI,IAACT;YAAqB,WAAWE;sBAC5BO;;IAGb;AACJ"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@squide/firefly-rsbuild-storybook",
3
3
  "author": "Workleap",
4
- "version": "1.0.5",
4
+ "version": "2.0.1",
5
5
  "description": "Squide firefly helpers for Storybook and Rsbuild.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -29,14 +29,14 @@
29
29
  "peerDependencies": {
30
30
  "@opentelemetry/api": "^1.9.0",
31
31
  "@rsbuild/core": "^1.7.2",
32
- "@tanstack/react-query": "^5.90.17",
32
+ "@tanstack/react-query": "^5.90.20",
33
33
  "launchdarkly-js-client-sdk": "^3.9.0",
34
34
  "msw": "^2.12.7",
35
35
  "react": "^18.0.0 || ^19.0.0",
36
36
  "react-dom": "^18.0.0 || ^19.0.0",
37
- "react-router": "^7.12.0",
38
- "storybook": "^10.1.11",
39
- "storybook-react-rsbuild": "^3.2.1"
37
+ "react-router": "^7.13.0",
38
+ "storybook": "^10.2.1",
39
+ "storybook-react-rsbuild": "^3.2.2"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@opentelemetry/api": {
@@ -46,33 +46,35 @@
46
46
  "dependencies": {
47
47
  "@workleap-telemetry/core": "^1.0.7",
48
48
  "@workleap/logging": "^1.3.4",
49
- "@squide/env-vars": "1.4.13",
50
- "@squide/firefly": "16.1.5",
51
- "@squide/launch-darkly": "1.0.4",
52
- "@squide/msw": "4.0.11"
49
+ "@squide/env-vars": "1.4.15",
50
+ "@squide/firefly": "16.1.7",
51
+ "@squide/launch-darkly": "1.0.6",
52
+ "@squide/msw": "4.0.13"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@eslint/js": "9.39.2",
56
56
  "@rsbuild/core": "1.7.2",
57
- "@rslib/core": "0.19.2",
58
- "@types/react": "19.2.8",
57
+ "@rslib/core": "0.19.3",
58
+ "@testing-library/react": "16.3.2",
59
+ "@types/react": "19.2.10",
59
60
  "@types/react-dom": "19.2.3",
60
- "@typescript-eslint/parser": "8.53.0",
61
+ "@typescript-eslint/parser": "8.54.0",
62
+ "@typescript/native-preview": "7.0.0-dev.20260128.1",
61
63
  "@vitejs/plugin-react": "5.1.2",
62
- "@workleap/eslint-configs": "1.1.9",
63
- "@workleap/rslib-configs": "1.1.5",
64
+ "@workleap/eslint-configs": "1.1.11",
65
+ "@workleap/rslib-configs": "1.1.6",
64
66
  "@workleap/typescript-configs": "3.0.7",
65
67
  "eslint": "9.39.2",
66
- "happy-dom": "20.3.0",
68
+ "happy-dom": "20.3.9",
67
69
  "typescript": "5.9.3",
68
- "typescript-eslint": "8.53.0",
69
- "vitest": "4.0.17"
70
+ "typescript-eslint": "8.54.0",
71
+ "vitest": "4.0.18"
70
72
  },
71
73
  "sideEffects": false,
72
74
  "scripts": {
73
75
  "build": "rslib build --config ./rslib.build.ts",
74
76
  "eslint": "eslint . --max-warnings=0 --cache --cache-location node_modules/.cache/eslint",
75
- "typecheck": "tsc",
77
+ "typecheck": "tsgo",
76
78
  "test": "vitest --config vitest.config.ts --no-watch"
77
79
  }
78
80
  }
@@ -1,16 +1,18 @@
1
1
  import { EnvironmentVariables, EnvironmentVariablesPlugin } from "@squide/env-vars";
2
2
  import { FireflyRuntime, InMemoryLaunchDarklyClient, LaunchDarklyPlugin, ModuleRegisterFunction, PluginFactory, toLocalModuleDefinitions } from "@squide/firefly";
3
+ import { FeatureFlags } from "@squide/launch-darkly";
3
4
  import { MswPlugin } from "@squide/msw";
4
5
  import { RootLogger } from "@workleap/logging";
5
- import { LDClient, LDFlagValue } from "launchdarkly-js-client-sdk";
6
+ import { LDClient } from "launchdarkly-js-client-sdk";
6
7
  import { StorybookRuntime } from "./StorybookRuntime.ts";
7
8
 
8
9
  export interface InitializeFireflyForStorybookOptions {
9
10
  localModules?: ModuleRegisterFunction<FireflyRuntime>[];
10
11
  environmentVariables?: EnvironmentVariables;
11
- featureFlags?: Map<string, LDFlagValue> | Record<string, LDFlagValue>;
12
+ featureFlags?: Partial<FeatureFlags>;
12
13
  launchDarklyClient?: LDClient;
13
14
  loggers?: RootLogger[];
15
+ useMsw?: boolean;
14
16
  }
15
17
 
16
18
  function logInitializationState(
@@ -22,7 +24,8 @@ function logInitializationState(
22
24
  localModules,
23
25
  environmentVariables,
24
26
  featureFlags,
25
- launchDarklyClient
27
+ launchDarklyClient,
28
+ useMsw
26
29
  } = options;
27
30
  const scope = (runtime.logger as RootLogger).startScope("[squide] Initializing the application.");
28
31
 
@@ -36,7 +39,7 @@ function logInitializationState(
36
39
  .information();
37
40
  }
38
41
 
39
- scope.information("[squide] Use MSW: Yes");
42
+ scope.information(`[squide] Use MSW: ${useMsw ? "Yes" : "No"}`);
40
43
 
41
44
  if (environmentVariables && Object.keys(environmentVariables).length > 0) {
42
45
  scope
@@ -72,30 +75,25 @@ function logInitializationState(
72
75
 
73
76
  export async function initializeFireflyForStorybook(options: InitializeFireflyForStorybookOptions = {}) {
74
77
  const {
75
- localModules,
78
+ localModules = [],
76
79
  environmentVariables,
77
80
  featureFlags = {},
78
81
  launchDarklyClient,
79
- loggers
82
+ loggers,
83
+ useMsw = true
80
84
  } = options;
81
85
 
82
86
  const plugins: PluginFactory<FireflyRuntime>[] = [
83
- x => new MswPlugin(x),
84
87
  x => new EnvironmentVariablesPlugin(x, {
85
88
  variables: environmentVariables
86
89
  }),
87
- x => new LaunchDarklyPlugin(
88
- x,
89
- // 1- If no client is provided create it.
90
- // 2- If feature flags are provided and it's an instance of Map, use the argument.
91
- // 3- If feature flags are provided (or the default value) and it's an object literal, convert the object to a Map instance.
92
- launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags instanceof Map
93
- ? featureFlags
94
- : new Map<string, LDFlagValue>(Object.entries(featureFlags))
95
- )
96
- )
90
+ x => new LaunchDarklyPlugin(x, launchDarklyClient ?? new InMemoryLaunchDarklyClient(featureFlags))
97
91
  ];
98
92
 
93
+ if (useMsw) {
94
+ plugins.push(x => new MswPlugin(x));
95
+ }
96
+
99
97
  const runtime = new StorybookRuntime({
100
98
  mode: "development",
101
99
  plugins,
@@ -104,10 +102,16 @@ export async function initializeFireflyForStorybook(options: InitializeFireflyFo
104
102
 
105
103
  logInitializationState(runtime, options, plugins);
106
104
 
107
- if (localModules && localModules.length > 0) {
105
+ if (localModules.length > 0) {
108
106
  await runtime.moduleManager.registerModules([
109
107
  ...toLocalModuleDefinitions(localModules)
110
108
  ]);
109
+ } else {
110
+ // There's a possibility that either no local modules are provided.
111
+ // This use case is hard to catch at the module manager level because it could also
112
+ // mean that the registration process hasn't started yet.
113
+ // This is safer to handle it here because we got all the modules functions.
114
+ runtime.moduleManager.setAsReady();
111
115
  }
112
116
 
113
117
  return runtime;
@@ -1,47 +1,47 @@
1
- import { LDFlagValue } from "launchdarkly-js-client-sdk";
2
- import { PropsWithChildren, useLayoutEffect } from "react";
1
+ import { FeatureFlags, isEditableLaunchDarklyClient, LaunchDarklyClientTransaction, useLaunchDarklyClient } from "@squide/launch-darkly";
2
+ import { PropsWithChildren, useEffect, useRef } from "react";
3
3
  import { Decorator } from "storybook-react-rsbuild";
4
4
 
5
5
  interface OverrideFeatureFlagsProps extends PropsWithChildren {
6
- featureFlags: Map<string, LDFlagValue>;
7
- overrides: Record<string, LDFlagValue>;
6
+ overrides: Partial<FeatureFlags>;
8
7
  }
9
8
 
9
+ // Used to override the flags in a "useEffect" hook but then the unit test were not working.
10
+ // To fix the unit tests, the flags are now overrided directly in the render function.
10
11
  function OverrideFeatureFlags(props: OverrideFeatureFlagsProps) {
11
12
  const {
12
- featureFlags,
13
13
  overrides,
14
14
  children
15
15
  } = props;
16
16
 
17
- // Must use a layout effect to override the feature flags before the story renders.
18
- useLayoutEffect(() => {
19
- const originalValues: Record<string, LDFlagValue> = {};
17
+ const transactionRef = useRef<LaunchDarklyClientTransaction | undefined>(undefined);
18
+ const client = useLaunchDarklyClient();
20
19
 
21
- Object.entries(overrides).forEach(([key, value]) => {
22
- originalValues[key] = featureFlags.get(key);
23
- featureFlags.set(key, value);
24
- });
20
+ // eslint-disable-next-line react-hooks/refs
21
+ if (!transactionRef.current) {
22
+ if (!isEditableLaunchDarklyClient(client)) {
23
+ throw new Error("[squide] The withFeatureFlagsOverrideDecorator hook can only be used with an EditableLaunchDarklyClient instance.");
24
+ }
25
25
 
26
+ transactionRef.current = client.startTransaction();
27
+ client.setFeatureFlags(overrides);
28
+ }
29
+
30
+ useEffect(() => {
26
31
  return () => {
27
32
  // Reset the feature flags to the original values.
28
- Object.entries(originalValues).forEach(([key, value]) => {
29
- featureFlags.set(key, value);
30
- });
33
+ transactionRef.current?.undo();
34
+ transactionRef.current = undefined;
31
35
  };
32
- }, [featureFlags, overrides]);
36
+ }, [transactionRef]);
33
37
 
34
38
  return children;
35
39
  }
36
40
 
37
- export function withFeatureFlagsOverrideDecorator<const T extends string>(featureFlags: Map<T, LDFlagValue>, overrides: Partial<Record<T, LDFlagValue>>): Decorator {
38
- if (!(featureFlags instanceof Map)) {
39
- throw new TypeError("[squide] The \"featureFlags\" argument must be a Map instance.");
40
- }
41
-
41
+ export function withFeatureFlagsOverrideDecorator(overrides: Partial<FeatureFlags>): Decorator {
42
42
  return story => {
43
43
  return (
44
- <OverrideFeatureFlags featureFlags={featureFlags} overrides={overrides}>
44
+ <OverrideFeatureFlags overrides={overrides}>
45
45
  {story()}
46
46
  </OverrideFeatureFlags>
47
47
  );