Optimize rebuilds in long-running processes
This commit is contained in:
parent
d3606b76dc
commit
ef149cfafb
8 changed files with 122 additions and 139 deletions
|
|
@ -53,6 +53,10 @@ function futureFlagsAvailable(config) {
|
|||
}
|
||||
|
||||
export function issueFlagNotices(config) {
|
||||
if (process.env.JEST_WORKER_ID !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const log = {
|
||||
info(messages) {
|
||||
console.log('')
|
||||
|
|
|
|||
|
|
@ -89,10 +89,30 @@ const cloneRuleWithParent = useMemo(
|
|||
rule => rule
|
||||
)
|
||||
|
||||
function buildUtilityMap(css) {
|
||||
function buildUtilityMap(css, lookupTree) {
|
||||
let index = 0
|
||||
const utilityMap = {}
|
||||
|
||||
lookupTree.walkRules(rule => {
|
||||
const utilityNames = extractUtilityNames(rule.selector)
|
||||
|
||||
utilityNames.forEach((utilityName, i) => {
|
||||
if (utilityMap[utilityName] === undefined) {
|
||||
utilityMap[utilityName] = []
|
||||
}
|
||||
|
||||
utilityMap[utilityName].push({
|
||||
index,
|
||||
utilityName,
|
||||
classPosition: i,
|
||||
get rule() {
|
||||
return cloneRuleWithParent(rule)
|
||||
},
|
||||
})
|
||||
index++
|
||||
})
|
||||
})
|
||||
|
||||
css.walkRules(rule => {
|
||||
const utilityNames = extractUtilityNames(rule.selector)
|
||||
|
||||
|
|
@ -151,8 +171,8 @@ function mergeAdjacentRules(initialRule, rulesToInsert) {
|
|||
return rulesToInsert.filter(r => r.nodes.length > 0)
|
||||
}
|
||||
|
||||
function makeExtractUtilityRules(css, config) {
|
||||
const utilityMap = buildUtilityMap(css)
|
||||
function makeExtractUtilityRules(css, lookupTree, config) {
|
||||
const utilityMap = buildUtilityMap(css, lookupTree)
|
||||
|
||||
return function extractUtilityRules(utilityNames, rule) {
|
||||
const combined = []
|
||||
|
|
@ -182,7 +202,7 @@ function makeExtractUtilityRules(css, config) {
|
|||
}
|
||||
|
||||
function processApplyAtRules(css, lookupTree, config) {
|
||||
const extractUtilityRules = makeExtractUtilityRules(lookupTree, config)
|
||||
const extractUtilityRules = makeExtractUtilityRules(css, lookupTree, config)
|
||||
|
||||
do {
|
||||
css.walkRules(rule => {
|
||||
|
|
@ -259,7 +279,9 @@ function processApplyAtRules(css, lookupTree, config) {
|
|||
return css
|
||||
}
|
||||
|
||||
export default function applyComplexClasses(config, getProcessedPlugins) {
|
||||
let defaultTailwindTree = null
|
||||
|
||||
export default function applyComplexClasses(config, getProcessedPlugins, configChanged) {
|
||||
return function(css) {
|
||||
// We can stop already when we don't have any @apply rules. Vue users: you're welcome!
|
||||
if (!hasAtRule(css, 'apply')) {
|
||||
|
|
@ -268,31 +290,39 @@ export default function applyComplexClasses(config, getProcessedPlugins) {
|
|||
|
||||
// Tree already contains @tailwind rules, don't prepend default Tailwind tree
|
||||
if (hasAtRule(css, 'tailwind')) {
|
||||
return processApplyAtRules(css, css, config)
|
||||
return processApplyAtRules(css, postcss.root(), config)
|
||||
}
|
||||
|
||||
// Tree contains no @tailwind rules, so generate all of Tailwind's styles and
|
||||
// prepend them to the user's CSS. Important for <style> blocks in Vue components.
|
||||
return postcss([
|
||||
substituteTailwindAtRules(config, getProcessedPlugins()),
|
||||
evaluateTailwindFunctions(config),
|
||||
substituteVariantsAtRules(config, getProcessedPlugins()),
|
||||
substituteResponsiveAtRules(config),
|
||||
convertLayerAtRulesToControlComments(config),
|
||||
substituteScreenAtRules(config),
|
||||
])
|
||||
.process(
|
||||
`
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
`,
|
||||
{ from: undefined }
|
||||
)
|
||||
.then(result => {
|
||||
// Prepend Tailwind's generated classes to the tree so they are available for `@apply`
|
||||
const lookupTree = _.tap(result.root, tree => tree.append(css.clone()))
|
||||
return processApplyAtRules(css, lookupTree, config)
|
||||
})
|
||||
const generateLookupTree =
|
||||
configChanged || defaultTailwindTree === null
|
||||
? () => {
|
||||
return postcss([
|
||||
substituteTailwindAtRules(config, getProcessedPlugins()),
|
||||
evaluateTailwindFunctions(config),
|
||||
substituteVariantsAtRules(config, getProcessedPlugins()),
|
||||
substituteResponsiveAtRules(config),
|
||||
convertLayerAtRulesToControlComments(config),
|
||||
substituteScreenAtRules(config),
|
||||
])
|
||||
.process(
|
||||
`
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
`,
|
||||
{ from: undefined }
|
||||
)
|
||||
.then(result => {
|
||||
defaultTailwindTree = result
|
||||
return defaultTailwindTree
|
||||
})
|
||||
}
|
||||
: () => Promise.resolve(defaultTailwindTree)
|
||||
|
||||
return generateLookupTree().then(result => {
|
||||
return processApplyAtRules(css, result.root, config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,14 +56,19 @@ function findClass(classToApply, classTable, onError) {
|
|||
return match.clone().nodes
|
||||
}
|
||||
|
||||
export default function(config, getProcessedPlugins) {
|
||||
let shadowLookup = null
|
||||
|
||||
export default function(config, getProcessedPlugins, configChanged) {
|
||||
if (flagEnabled(config, 'applyComplexClasses')) {
|
||||
return applyComplexClasses(config, getProcessedPlugins)
|
||||
return applyComplexClasses(config, getProcessedPlugins, configChanged)
|
||||
}
|
||||
|
||||
return function(css) {
|
||||
const classLookup = buildClassTable(css)
|
||||
const shadowLookup = buildShadowTable(getProcessedPlugins().utilities)
|
||||
shadowLookup =
|
||||
configChanged || !shadowLookup
|
||||
? buildShadowTable(getProcessedPlugins().utilities)
|
||||
: shadowLookup
|
||||
|
||||
css.walkRules(rule => {
|
||||
rule.walkAtRules('apply', atRule => {
|
||||
|
|
|
|||
|
|
@ -16,25 +16,33 @@ import processPlugins from './util/processPlugins'
|
|||
import cloneNodes from './util/cloneNodes'
|
||||
import { issueFlagNotices } from './featureFlags.js'
|
||||
|
||||
import hash from 'object-hash'
|
||||
|
||||
let flagsIssued = null
|
||||
let previousConfig = null
|
||||
let processedPlugins = null
|
||||
let getProcessedPlugins = null
|
||||
|
||||
export default function(getConfig) {
|
||||
return function(css) {
|
||||
const config = getConfig()
|
||||
const configChanged = hash(previousConfig) !== hash(config)
|
||||
previousConfig = config
|
||||
|
||||
if (!flagsIssued || !_.isEqual(flagsIssued, _.pick(config, ['future', 'experimental']))) {
|
||||
flagsIssued = _.pick(config, ['future', 'experimental'])
|
||||
issueFlagNotices(config)
|
||||
}
|
||||
|
||||
const processedPlugins = processPlugins([...corePlugins(config), ...config.plugins], config)
|
||||
|
||||
const getProcessedPlugins = function() {
|
||||
return {
|
||||
...processedPlugins,
|
||||
base: cloneNodes(processedPlugins.base),
|
||||
components: cloneNodes(processedPlugins.components),
|
||||
utilities: cloneNodes(processedPlugins.utilities),
|
||||
if (configChanged) {
|
||||
processedPlugins = processPlugins([...corePlugins(config), ...config.plugins], config)
|
||||
getProcessedPlugins = function() {
|
||||
return {
|
||||
...processedPlugins,
|
||||
base: cloneNodes(processedPlugins.base),
|
||||
components: cloneNodes(processedPlugins.components),
|
||||
utilities: cloneNodes(processedPlugins.utilities),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +53,7 @@ export default function(getConfig) {
|
|||
substituteResponsiveAtRules(config),
|
||||
convertLayerAtRulesToControlComments(config),
|
||||
substituteScreenAtRules(config),
|
||||
substituteClassApplyAtRules(config, getProcessedPlugins),
|
||||
substituteClassApplyAtRules(config, getProcessedPlugins, configChanged),
|
||||
applyImportantConfiguration(config),
|
||||
purgeUnusedStyles(config),
|
||||
]).process(css, { from: _.get(css, 'source.input.file') })
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue