Skip to content
This repository was archived by the owner on Apr 6, 2021. It is now read-only.

Commit 7af9dc9

Browse files
committed
Clone nodes to prevent css-loader from mutating the rule cache
We have a rule cache that we reuse — in contrast to normal tailwind which generates fresh rules every run. This is fine when rules are treated as immutable objects. However, aditional postcss plugins may not honor this contract. For example, if you use css-loader and have url rewriting turned on (and it is by default) then css-loader will mutate the rules it gets from postcss-loader as it has the ability to use the postcss AST directly. This saves on memory but in our case causes an issue because these rule objects point directly to our cache. Here we combat this by cloning the rules that go directly into the root node.
1 parent a46d0d9 commit 7af9dc9

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

src/lib/expandTailwindAtRules.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs')
22
const fastGlob = require('fast-glob')
33
const sharedState = require('./sharedState')
44
const { generateRules } = require('./generateRules')
5-
const { bigSign } = require('./utils')
5+
const { bigSign, cloneNodes } = require('./utils')
66

77
let env = sharedState.env
88
let contentMatchCache = sharedState.contentMatchCache
@@ -179,25 +179,25 @@ function expandTailwindAtRules(context, registerDependency) {
179179
// Replace any Tailwind directives with generated CSS
180180

181181
if (layerNodes.base) {
182-
layerNodes.base.before([...baseNodes])
182+
layerNodes.base.before(cloneNodes([...baseNodes]))
183183
layerNodes.base.remove()
184184
}
185185

186186
if (layerNodes.components) {
187-
layerNodes.components.before([...componentNodes])
187+
layerNodes.components.before(cloneNodes([...componentNodes]))
188188
layerNodes.components.remove()
189189
}
190190

191191
if (layerNodes.utilities) {
192-
layerNodes.utilities.before([...utilityNodes])
192+
layerNodes.utilities.before(cloneNodes([...utilityNodes]))
193193
layerNodes.utilities.remove()
194194
}
195195

196196
if (layerNodes.screens) {
197-
layerNodes.screens.before([...screenNodes])
197+
layerNodes.screens.before(cloneNodes([...screenNodes]))
198198
layerNodes.screens.remove()
199199
} else {
200-
root.append([...screenNodes])
200+
root.append(cloneNodes([...screenNodes]))
201201
}
202202

203203
// ---

src/lib/utils.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,24 @@ function nameClass(...args) {
8484
return escapeCommas(tailwindUtils.nameClass(...args))
8585
}
8686

87+
/**
88+
* Clone generated and/or cached nodes to ensure no future
89+
* postcss plugins can mutate the rules and mess up our cache
90+
*
91+
* NOTE: Only clone the nodes you pass to root.append()
92+
*
93+
* @param {import('postcss').Node[]} nodes
94+
* */
95+
function cloneNodes(nodes) {
96+
return nodes.map(node => node.clone())
97+
}
98+
8799
module.exports = {
88100
toPostCssNode,
89101
bigSign,
90102
isPlainObject,
91103
escapeClassName,
92104
escapeCommas,
93105
nameClass,
106+
cloneNodes,
94107
}

0 commit comments

Comments
 (0)