diff --git a/apps/designer/app/entry.client.tsx b/apps/designer/app/entry.client.tsx index 0a870b7dab..af4f800575 100644 --- a/apps/designer/app/entry.client.tsx +++ b/apps/designer/app/entry.client.tsx @@ -5,6 +5,7 @@ import { initSentry } from "./shared/sentry"; import { useLocation, useMatches } from "@remix-run/react"; import { BrowserTracing, remixRouterInstrumentation } from "@sentry/remix"; import { useEffect } from "react"; +import env from "./shared/env"; initSentry({ integrations: [ @@ -18,4 +19,14 @@ initSentry({ ], }); +// Forward DEBUG env variable defined on the server to the client-side debug() utility. +// This way you can set it once server-side and have the logging or feature flags enabled on both, server and client +if ( + typeof localStorage !== "undefined" && + // Client-side value should override the server-side one + (localStorage.debug == null || localStorage.debug === "") +) { + localStorage.debug = env.DEBUG; +} + hydrate(, document); diff --git a/apps/designer/app/env.server.ts b/apps/designer/app/env.server.ts index 90fa727197..7040eecefb 100644 --- a/apps/designer/app/env.server.ts +++ b/apps/designer/app/env.server.ts @@ -3,6 +3,8 @@ const env = { SENTRY_DSN: process.env.SENTRY_DSN, VERCEL_ENV: process.env.VERCEL_ENV, + DEBUG: process.env.DEBUG, + FEATURE: process.env.FEATURE, } as const; export default env; diff --git a/apps/designer/app/shared/feature-flags/feature.ts b/apps/designer/app/shared/feature-flags/feature.ts new file mode 100644 index 0000000000..c000c73826 --- /dev/null +++ b/apps/designer/app/shared/feature-flags/feature.ts @@ -0,0 +1,32 @@ +import * as flags from "./flags"; +import env from "~/shared/env"; + +type Name = keyof typeof flags; + +const parse = (flags?: string | null): Array => + // Supports both, space and comma separated items + (flags ?? "").split(/\s|,/).filter(Boolean) as Array; + +const readLocal = (): Array => { + try { + const flags = localStorage.getItem("feature"); + return parse(flags); + } catch (_error) { + // Not having feature in localStorage or not having localStorage implemented, both should not throw. + } + return []; +}; + +/** + * Returns true/false if the feature is turned on. + * A feature can be turned on: + * - by default directly in ./flags + * - by providing an environment variable server-side (locally or on the server): FEATURE="something1, something2" yarn dev + * - by setting it in the browser console: localStorage.feature = 'something1, something2', browser defined flag will override server-side flag + */ +export const isFeatureEnabled = (name: Name): boolean => { + const defaultValue = flags[name]; + const envValue = parse(env.FEATURE).includes(name); + const localValue = readLocal().includes(name); + return localValue ?? envValue ?? defaultValue; +}; diff --git a/apps/designer/app/shared/feature-flags/flags.ts b/apps/designer/app/shared/feature-flags/flags.ts new file mode 100644 index 0000000000..fe68be041d --- /dev/null +++ b/apps/designer/app/shared/feature-flags/flags.ts @@ -0,0 +1 @@ +export const theme = false; diff --git a/apps/designer/app/shared/feature-flags/index.ts b/apps/designer/app/shared/feature-flags/index.ts new file mode 100644 index 0000000000..d0d5122433 --- /dev/null +++ b/apps/designer/app/shared/feature-flags/index.ts @@ -0,0 +1 @@ +export * from "./feature"; diff --git a/apps/designer/package.json b/apps/designer/package.json index bbda01c018..8614ff88a7 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -59,6 +59,7 @@ "@webstudio-is/sdk": "0.4.1", "bson-objectid": "^2.0.2", "camelcase": "^6.3.0", + "debug": "^4.3.4", "hyphenate-style-name": "^1.0.4", "immer": "^9.0.12", "immerhin": "^0.1.2",