diff --git a/CHANGELOG.md b/CHANGELOG.md index 981553e8083a..24ed1afc5059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure configured `font-feature-settings` for `mono` are included in Preflight ([#12342](https://github.com/tailwindlabs/tailwindcss/pull/12342)) - 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)) ## [3.3.5] - 2023-10-25 diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index 2933d6f3e44e..b78bf1e2b08b 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -265,6 +265,9 @@ export default function expandTailwindAtRules(context) { ) } + // 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 5add3b0c81e3..75bfd84fc7b7 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -485,7 +485,7 @@ crosscheck(({ stable, oxide }) => { '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( @@ -535,6 +535,11 @@ crosscheck(({ stable, oxide }) => { '3:12 -> 7:12', '4:14-35 -> 8:14-35', '5:12 -> 9:12', + '1:0 -> 10:12', + '3:12 -> 11:12', + '4:14-35 -> 12:14-35', + '5:12 -> 13:12', + '1:0 -> 14:0', ]) } )