/
context-reuse.test.js
163 lines (130 loc) · 5 KB
/
context-reuse.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
const fs = require('fs')
const path = require('path')
const postcss = require('postcss')
const tailwind = require('../src/index.js')
const sharedState = require('../src/lib/sharedState.js')
const configPath = path.resolve(__dirname, './context-reuse.tailwind.config.js')
const { css } = require('./util/run.js')
function run(input, config = {}, from = null) {
from = [
`${path.resolve(__filename)}?test=${expect.getState().currentTestName}`,
from
].join('&')
return postcss(tailwind(config)).process(input, { from })
}
beforeEach(async () => {
let config = {
content: [path.resolve(__dirname, './context-reuse.test.html')],
corePlugins: { preflight: false },
}
await fs.promises.writeFile(configPath, `module.exports = ${JSON.stringify(config)};`)
})
afterEach(async () => {
await fs.promises.unlink(configPath)
})
it('re-uses the context across multiple files with the same config', async () => {
let results = [
await run(`@tailwind utilities;`, configPath, `id=1`),
// Using @apply directives should still re-use the context
// They depend on the config but do not the other way around
await run(`body { @apply bg-blue-400; }`, configPath, `id=2`),
await run(`body { @apply text-red-400; }`, configPath, `id=3`),
await run(`body { @apply mb-4; }`, configPath, `id=4`),
]
let dependencies = results.map((result) => {
return result.messages
.filter((message) => message.type === 'dependency')
.map((message) => message.file)
})
// The content files don't have any utilities in them so this should be empty
expect(results[0].css).toMatchFormattedCss(css``)
// However, @apply is being used so we want to verify that they're being inlined into the CSS rules
expect(results[1].css).toMatchFormattedCss(css`
body {
--tw-bg-opacity: 1;
background-color: rgb(96 165 250 / var(--tw-bg-opacity));
}
`)
expect(results[2].css).toMatchFormattedCss(css`
body {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
`)
expect(results[3].css).toMatchFormattedCss(css`
body {
margin-bottom: 1rem;
}
`)
// Files with @tailwind directives depends on the PostCSS tree, config, AND any content files
expect(dependencies[0]).toEqual([
path.resolve(__dirname, 'context-reuse.test.html'),
path.resolve(__dirname, 'context-reuse.tailwind.config.js'),
])
// @apply depends only on the containing PostCSS tree *and* the config file but no content files
// as they cannot affect the outcome of the @apply directives
expect(dependencies[1]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')])
expect(dependencies[2]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')])
expect(dependencies[3]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')])
// And none of this should have resulted in multiple contexts being created
expect(sharedState.contextSourcesMap.size).toBe(1)
})
it('updates layers when any CSS containing @tailwind directives changes', async () => {
let result
// Compile the initial version once
let input = css`
@tailwind utilities;
@layer utilities {
.custom-utility {
color: orange;
}
}
`
result = await run(input, configPath, `id=1`)
expect(result.css).toMatchFormattedCss(css`
.only\:custom-utility:only-child {
color: orange;
}
`)
// Save the file with a change
input = css`
@tailwind utilities;
@layer utilities {
.custom-utility {
color: blue;
}
}
`
result = await run(input, configPath, `id=1`)
expect(result.css).toMatchFormattedCss(css`
.only\:custom-utility:only-child {
color: blue;
}
`)
})
it('invalidates the context when any CSS containing @tailwind directives changes', async () => {
sharedState.contextInvalidationCount = 0
sharedState.sourceHashMap.clear()
// Save the file a handful of times with no changes
// This builds the context at most once
for (let n = 0; n < 5; n++) {
await run(`@tailwind utilities;`, configPath, `id=1`)
}
expect(sharedState.contextInvalidationCount).toBe(1)
// Save the file twice with a change
// This should rebuild the context again but only once
await run(`@tailwind utilities; .foo {}`, configPath, `id=1`)
await run(`@tailwind utilities; .foo {}`, configPath, `id=1`)
expect(sharedState.contextInvalidationCount).toBe(2)
// Save the file twice with a content but not length change
// This should rebuild the context two more times
await run(`@tailwind utilities; .bar {}`, configPath, `id=1`)
await run(`@tailwind utilities; .baz {}`, configPath, `id=1`)
expect(sharedState.contextInvalidationCount).toBe(4)
// Save a file with a change that does not affect the context
// No invalidation should occur
await run(`.foo { @apply mb-1; }`, configPath, `id=2`)
await run(`.foo { @apply mb-1; }`, configPath, `id=2`)
await run(`.foo { @apply mb-1; }`, configPath, `id=2`)
expect(sharedState.contextInvalidationCount).toBe(4)
})