From 88e98f557c45499d3cbdc3889b81bcb0200b1e39 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Fri, 9 Sep 2022 17:45:25 +0200 Subject: [PATCH] Fix issue with Tailwind modifying global state (#9294) * Fix issue with Tailwind modifying global state When running Tailwind, it modifies the plugin defaults parameters. As a result Tailwind using a Tailwind plugin in the same process twice yields different results. * Add failing test * Undo defaults change * wip * Fix shared mutation problem * Update changelog Co-authored-by: Jordan Pittman --- CHANGELOG.md | 1 + src/util/resolveConfig.js | 2 +- tests/resolveConfig.test.js | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d549f81f9b2..003147be00c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don't output duplicate utilities ([#9208](https://github.com/tailwindlabs/tailwindcss/pull/9208)) - Fix `fontFamily` config TypeScript types ([#9214](https://github.com/tailwindlabs/tailwindcss/pull/9214)) - Handle variants on complex selector utilities ([#9262](https://github.com/tailwindlabs/tailwindcss/pull/9262)) +- Don't mutate shared config objects ([#9294](https://github.com/tailwindlabs/tailwindcss/pull/9294)) ## [3.1.8] - 2022-08-05 diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index 9a4845af5625..35b0cef5fe31 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -29,7 +29,7 @@ function mergeWith(target, ...sources) { if (merged === undefined) { if (isObject(target[k]) && isObject(source[k])) { - target[k] = mergeWith(target[k], source[k], customizer) + target[k] = mergeWith({}, target[k], source[k], customizer) } else { target[k] = source[k] } diff --git a/tests/resolveConfig.test.js b/tests/resolveConfig.test.js index 2c435e5a6eb3..60776a39a88b 100644 --- a/tests/resolveConfig.test.js +++ b/tests/resolveConfig.test.js @@ -1763,3 +1763,30 @@ test('all helpers can be destructured from the first function argument', () => { }, }) }) + +test('does not duplicate extended configs every time resolveConfig is called', () => { + let shared = { + foo: { bar: { baz: [{ color: 'red' }] } }, + } + + const createConfig = (color) => + resolveConfig([ + { + theme: { + foo: shared.foo, + extend: { + foo: { bar: { baz: { color } } }, + }, + }, + }, + ]) + + createConfig('orange') + createConfig('yellow') + createConfig('green') + + const result = createConfig('blue') + + expect(shared.foo.bar.baz).toMatchObject([{ color: 'red' }]) + expect(result.theme.foo.bar.baz).toMatchObject([{ color: 'red' }, { color: 'blue' }]) +})