diff --git a/.changeset/chilly-scissors-remember.md b/.changeset/chilly-scissors-remember.md new file mode 100644 index 0000000000..16a34568d4 --- /dev/null +++ b/.changeset/chilly-scissors-remember.md @@ -0,0 +1,5 @@ +--- +"@sumup-oss/circuit-ui": patch +--- + +Fixed safely accessing environment variables in environments where the `process` variable is undefined. diff --git a/packages/circuit-ui/components/Display/Display.tsx b/packages/circuit-ui/components/Display/Display.tsx index 43bfb21e0f..28c03d65e7 100644 --- a/packages/circuit-ui/components/Display/Display.tsx +++ b/packages/circuit-ui/components/Display/Display.tsx @@ -18,6 +18,7 @@ import { forwardRef, type HTMLAttributes } from 'react'; import { clsx } from '../../styles/clsx.js'; import { CircuitError } from '../../util/errors.js'; import { deprecate } from '../../util/logger.js'; +import { getEnvVariable } from '../../util/env.js'; import classes from './Display.module.css'; @@ -75,7 +76,7 @@ export const Display = forwardRef( if ( process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test' && - !process?.env?.UNSAFE_DISABLE_ELEMENT_ERRORS && + !getEnvVariable('UNSAFE_DISABLE_ELEMENT_ERRORS') && !as ) { throw new CircuitError('Display', 'The `as` prop is required.'); diff --git a/packages/circuit-ui/components/Headline/Headline.tsx b/packages/circuit-ui/components/Headline/Headline.tsx index a4b0e04064..1da5d60152 100644 --- a/packages/circuit-ui/components/Headline/Headline.tsx +++ b/packages/circuit-ui/components/Headline/Headline.tsx @@ -18,6 +18,7 @@ import { forwardRef, type HTMLAttributes } from 'react'; import { clsx } from '../../styles/clsx.js'; import { CircuitError } from '../../util/errors.js'; import { deprecate } from '../../util/logger.js'; +import { getEnvVariable } from '../../util/env.js'; import classes from './Headline.module.css'; @@ -68,7 +69,7 @@ export const Headline = forwardRef( if ( process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test' && - !process?.env?.UNSAFE_DISABLE_ELEMENT_ERRORS && + !getEnvVariable('UNSAFE_DISABLE_ELEMENT_ERRORS') && !as ) { throw new CircuitError('Headline', 'The `as` prop is required.'); diff --git a/packages/circuit-ui/util/env.spec.ts b/packages/circuit-ui/util/env.spec.ts new file mode 100644 index 0000000000..57177f7392 --- /dev/null +++ b/packages/circuit-ui/util/env.spec.ts @@ -0,0 +1,52 @@ +/** + * Copyright 2024, SumUp Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { beforeEach, describe, expect, it } from 'vitest'; + +import { getEnvVariable } from './env.js'; + +describe('env', () => { + describe('getEnvVariable', () => { + const originalProcess = process; + + beforeEach(() => { + process = originalProcess; + process.env = {}; + }); + + it('should return the environment variable`', () => { + process.env.FOO = 'foo'; + const actual = getEnvVariable('FOO'); + expect(actual).toBe('foo'); + }); + + it('should return undefined if the environment variable is not defined', () => { + const actual = getEnvVariable('FOO'); + expect(actual).toBeUndefined(); + }); + + it('should return undefined if `process` is not defined', () => { + // @ts-expect-error We're testing for this error + process = undefined; + const actual = getEnvVariable('FOO'); + expect(actual).toBeUndefined(); + }); + + it('should throw an error when used for `NODE_ENV`', () => { + const actual = () => getEnvVariable('NODE_ENV'); + expect(actual).toThrowError(); + }); + }); +}); diff --git a/packages/circuit-ui/util/env.ts b/packages/circuit-ui/util/env.ts new file mode 100644 index 0000000000..6740b481c4 --- /dev/null +++ b/packages/circuit-ui/util/env.ts @@ -0,0 +1,29 @@ +/** + * Copyright 2024, SumUp Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function getEnvVariable(name: string) { + if (name === 'NODE_ENV') { + // Some bundlers have special logic for `process.env.NODE_ENV` which + // relies on it being written as a continuous string. Destructuring or + // dynamically accessing `NODE_ENV` can break this logic. + throw new Error('Do not dynamically access NODE_ENV'); + } + + if (typeof process !== 'undefined') { + return process.env?.[name]; + } + + return undefined; +}