Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve @apply performance #3718

Merged
merged 2 commits into from Apr 30, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 65 additions & 19 deletions src/lib/substituteClassApplyAtRules.js
Expand Up @@ -102,8 +102,8 @@ const cloneRuleWithParent = useMemo(
(rule) => rule
)

function buildUtilityMap(css, lookupTree) {
let index = 0
function buildCssUtilityMap(css, startIndex) {
let index = startIndex
const utilityMap = {}

function handle(getRule, rule) {
Expand All @@ -124,16 +124,6 @@ function buildUtilityMap(css, lookupTree) {
})
}

// Lookup tree is the big lookup tree, making the rule lazy allows us to save
// some memory because we don't need everything.
lookupTree.walkRules(
handle.bind(null, (rule) => ({
get rule() {
return cloneRuleWithParent(rule)
},
}))
)

// This is the end user's css. This might contain rules that we want to
// apply. We want immediate copies of everything in case that we have user
// defined classes that are recursively applied. Down below we are modifying
Expand All @@ -145,6 +135,44 @@ function buildUtilityMap(css, lookupTree) {
return utilityMap
}

const buildLookupTreeUtilityMap = useMemo(
(lookupTree) => {
let index = 0
const utilityMap = {}

function handle(getRule, rule) {
const utilityNames = extractUtilityNames(rule.selector)

utilityNames.forEach((utilityName, i) => {
if (utilityMap[utilityName] === undefined) {
utilityMap[utilityName] = []
}

utilityMap[utilityName].push({
index,
utilityName,
classPosition: i,
...getRule(rule),
})
index++
})
}

// Lookup tree is the big lookup tree, making the rule lazy allows us to save
// some memory because we don't need everything.
lookupTree.walkRules(
handle.bind(null, (rule) => ({
get rule() {
return cloneRuleWithParent(rule)
},
}))
)

return utilityMap
},
(tree) => tree
)

function mergeAdjacentRules(initialRule, rulesToInsert) {
let previousRule = initialRule

Expand Down Expand Up @@ -181,23 +209,41 @@ function mergeAdjacentRules(initialRule, rulesToInsert) {
}

function makeExtractUtilityRules(css, lookupTree, config) {
const utilityMap = buildUtilityMap(css, lookupTree)
const lookupTreeUtilityMap = buildLookupTreeUtilityMap(lookupTree)
const lookupTreeUtilityMapKeys = Object.keys(lookupTreeUtilityMap)
const utilityMap = buildCssUtilityMap(css, lookupTreeUtilityMapKeys.length)

function getUtility(utilityName) {
const utility = []
if (lookupTreeUtilityMap[utilityName]) {
utility.push(...lookupTreeUtilityMap[utilityName])
}
if (utilityMap[utilityName]) {
utility.push(...utilityMap[utilityName])
}
if (utility.length > 0) return utility
}

return function extractUtilityRules(utilityNames, rule) {
const combined = []

utilityNames.forEach((utilityName) => {
if (utilityMap[utilityName] === undefined) {
const utility = getUtility(utilityName)
if (utility === undefined) {
// Look for prefixed utility in case the user has goofed
const prefixedUtility = prefixSelector(config.prefix, `.${utilityName}`).slice(1)
const prefixedUtilityName = prefixSelector(config.prefix, `.${utilityName}`).slice(1)

if (utilityMap[prefixedUtility] !== undefined) {
const prefixedUtility = getUtility(prefixedUtilityName)
if (prefixedUtility !== undefined) {
throw rule.error(
`The \`${utilityName}\` class does not exist, but \`${prefixedUtility}\` does. Did you forget the prefix?`
`The \`${utilityName}\` class does not exist, but \`${prefixedUtilityName}\` does. Did you forget the prefix?`
)
}

const suggestedClass = didYouMean(utilityName, Object.keys(utilityMap))
const suggestedClass = didYouMean(
utilityName,
Object.keys(utilityMap).concat(lookupTreeUtilityMapKeys)
)
const suggestionMessage = suggestedClass ? `, but \`${suggestedClass}\` does` : ''

throw rule.error(
Expand All @@ -206,7 +252,7 @@ function makeExtractUtilityRules(css, lookupTree, config) {
)
}

combined.push(...utilityMap[utilityName])
combined.push(...utility)
})

return combined.sort((a, b) => a.index - b.index)
Expand Down