From 317fba1d6bf3a70615664b28c57d783ab3314753 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 1 Dec 2023 11:05:34 -0500 Subject: [PATCH] Fix source maps of variant utilities that come from an `@layer` rule (#12508) * Refactor * Keep traversing sibling nodes * Make sure the root node has a source location for the end * Add source map test for at-rule variants * Update changelog --- CHANGELOG.md | 1 + src/lib/expandTailwindAtRules.js | 3 ++ src/util/cloneNodes.js | 49 +++++++++++++++++++++++--------- tests/source-maps.test.js | 7 ++++- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ad6b7485a0..2eaa047bc86e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improve candidate detection in minified JS arrays (without spaces) ([#12396](https://github.com/tailwindlabs/tailwindcss/pull/12396)) - Don't crash when given applying a variant to a negated version of a simple utility ([#12514](https://github.com/tailwindlabs/tailwindcss/pull/12514)) - Fix support for slashes in arbitrary modifiers ([#12515](https://github.com/tailwindlabs/tailwindcss/pull/12515)) +- Fix source maps of variant utilities that come from an `@layer` rule ([#12508](https://github.com/tailwindlabs/tailwindcss/pull/12508)) - [Oxide] Remove `autoprefixer` dependency ([#11315](https://github.com/tailwindlabs/tailwindcss/pull/11315)) - [Oxide] Fix source maps issue resulting in a crash ([#11319](https://github.com/tailwindlabs/tailwindcss/pull/11319)) - [Oxide] Fallback to RegEx based parser when using custom transformers or extractors ([#11335](https://github.com/tailwindlabs/tailwindcss/pull/11335)) diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index 7b4afaa8e1d8..ebbad5bd58a3 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -291,6 +291,9 @@ export default function expandTailwindAtRules(context) { root.append(cloned) } + // TODO: Why is the root node having no source location for `end` possible? + root.source.end = root.source.end ?? root.source.start + // If we've got a utility layer and no utilities are generated there's likely something wrong const hasUtilityVariants = variantNodes.some( (node) => node.raws.tailwind?.parentLayer === 'utilities' diff --git a/src/util/cloneNodes.js b/src/util/cloneNodes.js index 299dd63b3e2e..9fa1c14a99b7 100644 --- a/src/util/cloneNodes.js +++ b/src/util/cloneNodes.js @@ -1,21 +1,13 @@ +/** + * @param {import('postcss').Container[]} nodes + * @param {any} source + * @param {any} raws + * @returns {import('postcss').Container[]} + */ export default function cloneNodes(nodes, source = undefined, raws = undefined) { return nodes.map((node) => { let cloned = node.clone() - // We always want override the source map - // except when explicitly told not to - let shouldOverwriteSource = node.raws.tailwind?.preserveSource !== true || !cloned.source - - if (source !== undefined && shouldOverwriteSource) { - cloned.source = source - - if ('walk' in cloned) { - cloned.walk((child) => { - child.source = source - }) - } - } - if (raws !== undefined) { cloned.raws.tailwind = { ...cloned.raws.tailwind, @@ -23,6 +15,35 @@ export default function cloneNodes(nodes, source = undefined, raws = undefined) } } + if (source !== undefined) { + traverse(cloned, (node) => { + // Do not traverse nodes that have opted + // to preserve their original source + let shouldPreserveSource = node.raws.tailwind?.preserveSource === true && node.source + if (shouldPreserveSource) { + return false + } + + // Otherwise we can safely replace the source + // And continue traversing + node.source = source + }) + } + return cloned }) } + +/** + * Traverse a tree of nodes and don't traverse children if the callback + * returns false. Ideally we'd use Container#walk instead of this + * function but it stops traversing siblings too. + * + * @param {import('postcss').Container} node + * @param {(node: import('postcss').Container) => boolean} onNode + */ +function traverse(node, onNode) { + if (onNode(node) !== false) { + node.each?.((child) => traverse(child, onNode)) + } +} diff --git a/tests/source-maps.test.js b/tests/source-maps.test.js index c4e60bbba097..a1e2a7dd8158 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -427,7 +427,7 @@ test('components have source maps', async () => { test('source maps for layer rules are not rewritten to point to @tailwind directives', async () => { let config = { - content: [{ raw: `font-normal foo hover:foo` }], + content: [{ raw: `font-normal foo hover:foo lg:foo` }], } let utilitiesFile = postcss.parse( @@ -481,6 +481,11 @@ test('source maps for layer rules are not rewritten to point to @tailwind direct "6:0 -> 6:0", "7:2-23 -> 7:2-23", "8:0 -> 8:0", + "10:0 -> 10:0", + "11:2 -> 11:2", + "12:4-25 -> 12:4-25", + "13:2 -> 13:2", + "14:0 -> 14:0", ] `) })