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

Commit 96850da

Browse files
authored
Merge pull request #10 from tailwindlabs/vue-support
Better multi-build support
2 parents a3fd913 + 53e5b52 commit 96850da

File tree

3 files changed

+75
-17
lines changed

3 files changed

+75
-17
lines changed

src/lib/expandTailwindAtRules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ function expandTailwindAtRules(context, registerDependency) {
206206
if (env.DEBUG) {
207207
console.log('Changed files: ', context.changedFiles.size)
208208
console.log('Potential classes: ', candidates.size)
209-
console.log('Active contexts: ', sharedState.contextMap.size)
209+
console.log('Active contexts: ', sharedState.contextSourcesMap.size)
210210
console.log('Content match entries', contentMatchCache.size)
211211
}
212212

src/lib/setupContext.js

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
const fs = require('fs')
2+
const url = require('url')
23
const os = require('os')
34
const path = require('path')
45
const crypto = require('crypto')
56
const chokidar = require('chokidar')
67
const postcss = require('postcss')
8+
const hash = require('object-hash')
79
const dlv = require('dlv')
810
const selectorParser = require('postcss-selector-parser')
911
const LRU = require('quick-lru')
@@ -21,6 +23,8 @@ const { isPlainObject } = require('./utils')
2123
const { isBuffer } = require('util')
2224

2325
let contextMap = sharedState.contextMap
26+
let configContextMap = sharedState.configContextMap
27+
let contextSourcesMap = sharedState.contextSourcesMap
2428
let env = sharedState.env
2529

2630
// Earmarks a directory for our touch files.
@@ -148,27 +152,29 @@ function getTailwindConfig(configOrPath) {
148152
let userConfigPath = resolveConfigPath(configOrPath)
149153

150154
if (userConfigPath !== null) {
151-
let [prevConfig, prevModified = -Infinity] = configPathCache.get(userConfigPath) ?? []
155+
let [prevConfig, prevModified = -Infinity, prevConfigHash] =
156+
configPathCache.get(userConfigPath) ?? []
152157
let modified = fs.statSync(userConfigPath).mtimeMs
153158

154159
// It hasn't changed (based on timestamp)
155160
if (modified <= prevModified) {
156-
return [prevConfig, userConfigPath]
161+
return [prevConfig, userConfigPath, prevConfigHash]
157162
}
158163

159164
// It has changed (based on timestamp), or first run
160165
delete require.cache[userConfigPath]
161166
let newConfig = resolveConfig(require(userConfigPath))
162-
configPathCache.set(userConfigPath, [newConfig, modified])
163-
return [newConfig, userConfigPath]
167+
let newHash = hash(newConfig)
168+
configPathCache.set(userConfigPath, [newConfig, modified, newHash])
169+
return [newConfig, userConfigPath, newHash]
164170
}
165171

166172
// It's a plain object, not a path
167173
let newConfig = resolveConfig(
168174
configOrPath.config === undefined ? configOrPath : configOrPath.config
169175
)
170176

171-
return [newConfig, null]
177+
return [newConfig, null, hash(newConfig)]
172178
}
173179

174180
let fileModifiedMap = new Map()
@@ -177,7 +183,8 @@ function trackModified(files) {
177183
let changed = false
178184

179185
for (let file of files) {
180-
let newModified = fs.statSync(file).mtimeMs
186+
let pathname = url.parse(file).pathname
187+
let newModified = fs.statSync(pathname).mtimeMs
181188

182189
if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
183190
changed = true
@@ -537,32 +544,68 @@ function cleanupContext(context) {
537544
// plugins) then return it
538545
function setupContext(configOrPath) {
539546
return (result, root) => {
547+
let foundTailwind = false
548+
549+
root.walkAtRules('tailwind', (rule) => {
550+
foundTailwind = true
551+
})
552+
540553
let sourcePath = result.opts.from
541-
let [tailwindConfig, userConfigPath] = getTailwindConfig(configOrPath)
554+
let [tailwindConfig, userConfigPath, tailwindConfigHash] = getTailwindConfig(configOrPath)
542555

543556
let contextDependencies = new Set()
544-
contextDependencies.add(sourcePath)
545557

546-
if (userConfigPath !== null) {
547-
contextDependencies.add(userConfigPath)
558+
// If there are no @tailwind rules, we don't consider this CSS file or it's dependencies
559+
// to be dependencies of the context. Can reuse the context even if they change.
560+
// We may want to think about `@layer` being part of this trigger too, but it's tough
561+
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
562+
// in another file since independent sources are effectively isolated.
563+
if (foundTailwind) {
564+
contextDependencies.add(sourcePath)
565+
for (let message of result.messages) {
566+
if (message.type === 'dependency') {
567+
contextDependencies.add(message.file)
568+
}
569+
}
548570
}
549571

550-
for (let message of result.messages) {
551-
if (message.type === 'dependency') {
552-
contextDependencies.add(message.file)
553-
}
572+
if (userConfigPath !== null) {
573+
contextDependencies.add(userConfigPath)
554574
}
555575

556576
let contextDependenciesChanged =
557577
trackModified([...contextDependencies]) || userConfigPath === null
558578

559579
process.env.DEBUG && console.log('Source path:', sourcePath)
580+
581+
// If this file already has a context in the cache and we don't need to
582+
// reset the context, return the cached context.
560583
if (contextMap.has(sourcePath) && !contextDependenciesChanged) {
561584
return contextMap.get(sourcePath)
562585
}
563586

587+
// If the config file used already exists in the cache, return that.
588+
if (!contextDependenciesChanged && configContextMap.has(tailwindConfigHash)) {
589+
let context = configContextMap.get(tailwindConfigHash)
590+
contextSourcesMap.get(context).add(sourcePath)
591+
contextMap.set(sourcePath, context)
592+
return context
593+
}
594+
595+
// If this source is in the context map, get the old context.
596+
// Remove this source from the context sources for the old context,
597+
// and clean up that context if no one else is using it. This can be
598+
// called by many processes in rapid succession, so we check for presence
599+
// first because the first process to run this code will wipe it out first.
564600
if (contextMap.has(sourcePath)) {
565-
cleanupContext(contextMap.get(sourcePath))
601+
let oldContext = contextMap.get(sourcePath)
602+
if (contextSourcesMap.has(oldContext)) {
603+
contextSourcesMap.get(oldContext).remove(sourcePath)
604+
if (contextSourcesMap.get(oldContext).size === 0) {
605+
contextSourcesMap.delete(oldContext)
606+
cleanupContext(oldContext)
607+
}
608+
}
566609
}
567610

568611
process.env.DEBUG && console.log('Setting up new context...')
@@ -579,7 +622,6 @@ function setupContext(configOrPath) {
579622
newPostCssNodeCache: new Map(),
580623
candidateRuleMap: new Map(),
581624
configPath: userConfigPath,
582-
sourcePath: sourcePath,
583625
tailwindConfig: tailwindConfig,
584626
configDependencies: new Set(),
585627
candidateFiles: Array.isArray(tailwindConfig.purge)
@@ -588,8 +630,22 @@ function setupContext(configOrPath) {
588630
variantMap: new Map(),
589631
stylesheetCache: null,
590632
}
633+
634+
// ---
635+
636+
// Update all context tracking state
637+
638+
configContextMap.set(tailwindConfigHash, context)
591639
contextMap.set(sourcePath, context)
592640

641+
if (!contextSourcesMap.has(context)) {
642+
contextSourcesMap.set(context, new Set())
643+
}
644+
645+
contextSourcesMap.get(context).add(sourcePath)
646+
647+
// ---
648+
593649
if (userConfigPath !== null) {
594650
for (let dependency of getModuleDependencies(userConfigPath)) {
595651
if (dependency.file === userConfigPath) {

src/lib/sharedState.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ module.exports = {
77
DEBUG: process.env.DEBUG !== undefined,
88
},
99
contextMap: new Map(),
10+
configContextMap: new Map(),
11+
contextSourcesMap: new Map(),
1012
contentMatchCache: new LRU({ maxSize: 25000 }),
1113
}

0 commit comments

Comments
 (0)