|
1 | 1 | let fs = require('fs')
|
2 | 2 | let path = require('path')
|
| 3 | +let { stripVTControlCharacters } = require('util') |
3 | 4 | let { cwd } = require('./cwd.js')
|
4 | 5 | let { writeConfigs, destroyConfigs } = require('./config.js')
|
5 | 6 |
|
6 | 7 | let $ = require('../../execute')
|
7 | 8 | let { css } = require('../../syntax')
|
8 | 9 |
|
9 |
| -let { readOutputFile } = require('../../io')({ |
| 10 | +let { writeInputFile, readOutputFile } = require('../../io')({ |
10 | 11 | output: 'dist',
|
11 | 12 | input: '.',
|
12 | 13 | })
|
@@ -37,14 +38,22 @@ async function build({ cwd: cwdPath } = {}) {
|
37 | 38 |
|
38 | 39 | await cwd.switch(cwdPath)
|
39 | 40 |
|
| 41 | + // Hide console.log and console.error output |
| 42 | + let consoleLogMock = jest.spyOn(console, 'log').mockImplementation(() => {}) |
| 43 | + let consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {}) |
| 44 | + |
40 | 45 | // Note that ./tailwind.config.js is hardcoded on purpose here
|
41 | 46 | // It represents a config but one that could be in different places
|
42 |
| - await $(`postcss ${inputPath} -o ${outputPath}`, { |
43 |
| - env: { NODE_ENV: 'production' }, |
| 47 | + let result = await $(`postcss ${inputPath} -o ${outputPath}`, { |
| 48 | + env: { NODE_ENV: 'production', JEST_WORKER_ID: undefined }, |
44 | 49 | cwd: cwdPath,
|
45 | 50 | })
|
46 | 51 |
|
| 52 | + consoleLogMock.mockRestore() |
| 53 | + consoleErrorMock.mockRestore() |
| 54 | + |
47 | 55 | return {
|
| 56 | + ...result, |
48 | 57 | css: await readOutputFile('main.css'),
|
49 | 58 | }
|
50 | 59 | }
|
@@ -160,6 +169,206 @@ it('it handles ignored globs correctly when not relative to the config', async (
|
160 | 169 | expect(result.css).toMatchCss(``)
|
161 | 170 | })
|
162 | 171 |
|
| 172 | +it('warns when globs are too broad and match node_modules', async () => { |
| 173 | + await writeConfigs({ |
| 174 | + both: { |
| 175 | + content: { |
| 176 | + files: ['./**/*.html'], |
| 177 | + }, |
| 178 | + }, |
| 179 | + }) |
| 180 | + |
| 181 | + let result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 182 | + |
| 183 | + // No issues yet, because we don't have a file that resolves inside `node_modules` |
| 184 | + expect(result.stderr).toEqual('') |
| 185 | + |
| 186 | + // We didn't scan any node_modules files yet |
| 187 | + expect(result.css).not.toIncludeCss( |
| 188 | + css` |
| 189 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 190 | + --tw-content: 'node_modules/bad.html'; |
| 191 | + content: var(--tw-content); |
| 192 | + } |
| 193 | + ` |
| 194 | + ) |
| 195 | + |
| 196 | + // Write a file that resolves inside `node_modules` |
| 197 | + await writeInputFile( |
| 198 | + 'node_modules/bad.html', |
| 199 | + String.raw`<div class="content-['node\_modules/bad.html']">Bad</div>` |
| 200 | + ) |
| 201 | + |
| 202 | + result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 203 | + |
| 204 | + // We still expect the node_modules file to be processed |
| 205 | + expect(result.css).toIncludeCss( |
| 206 | + css` |
| 207 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 208 | + --tw-content: 'node_modules/bad.html'; |
| 209 | + content: var(--tw-content); |
| 210 | + } |
| 211 | + ` |
| 212 | + ) |
| 213 | + |
| 214 | + // We didn't list `node_modules` in the glob explicitly, so we should see a |
| 215 | + // warning. |
| 216 | + expect(stripVTControlCharacters(result.stderr)).toMatchInlineSnapshot(` |
| 217 | + " |
| 218 | + warn - Your \`content\` configuration includes a pattern which looks like it's accidentally matching all of \`node_modules\` and can cause serious performance issues. |
| 219 | + warn - Pattern: \`./**/*.html\` |
| 220 | + warn - See our documentation for recommendations: |
| 221 | + warn - https://tailwindcss.com/docs/content-configuration#pattern-recommendations |
| 222 | + " |
| 223 | + `) |
| 224 | +}) |
| 225 | + |
| 226 | +it('should not warn when glob contains node_modules explicitly', async () => { |
| 227 | + await writeConfigs({ |
| 228 | + both: { |
| 229 | + content: { |
| 230 | + files: ['./node_modules/**/*.html'], |
| 231 | + }, |
| 232 | + }, |
| 233 | + }) |
| 234 | + |
| 235 | + let result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 236 | + |
| 237 | + // Write a file that resolves inside `node_modules` |
| 238 | + await writeInputFile( |
| 239 | + 'node_modules/bad.html', |
| 240 | + String.raw`<div class="content-['node\_modules/bad.html']">Bad</div>` |
| 241 | + ) |
| 242 | + |
| 243 | + result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 244 | + |
| 245 | + // We still expect the node_modules file to be processed |
| 246 | + expect(result.css).toIncludeCss( |
| 247 | + css` |
| 248 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 249 | + --tw-content: 'node_modules/bad.html'; |
| 250 | + content: var(--tw-content); |
| 251 | + } |
| 252 | + ` |
| 253 | + ) |
| 254 | + |
| 255 | + // We explicitly listed `node_modules` in the glob, so we shouldn't see a |
| 256 | + // warning. |
| 257 | + expect(result.stderr).toEqual('') |
| 258 | +}) |
| 259 | + |
| 260 | +it('should not warn when globs are too broad if other glob match node_modules explicitly', async () => { |
| 261 | + await writeConfigs({ |
| 262 | + both: { |
| 263 | + content: { |
| 264 | + files: ['./**/*.html', './node_modules/bad.html'], |
| 265 | + }, |
| 266 | + }, |
| 267 | + }) |
| 268 | + |
| 269 | + let result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 270 | + |
| 271 | + // No issues yet, because we don't have a file that resolves inside `node_modules` |
| 272 | + expect(result.stderr).toEqual('') |
| 273 | + |
| 274 | + // We didn't scan any node_modules files yet |
| 275 | + expect(result.css).not.toIncludeCss( |
| 276 | + css` |
| 277 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 278 | + --tw-content: 'node_modules/bad.html'; |
| 279 | + content: var(--tw-content); |
| 280 | + } |
| 281 | + ` |
| 282 | + ) |
| 283 | + |
| 284 | + // Write a file that resolves inside `node_modules` |
| 285 | + await writeInputFile( |
| 286 | + 'node_modules/bad.html', |
| 287 | + String.raw`<div class="content-['node\_modules/bad.html']">Bad</div>` |
| 288 | + ) |
| 289 | + |
| 290 | + result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 291 | + |
| 292 | + // We still expect the node_modules file to be processed |
| 293 | + expect(result.css).toIncludeCss( |
| 294 | + css` |
| 295 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 296 | + --tw-content: 'node_modules/bad.html'; |
| 297 | + content: var(--tw-content); |
| 298 | + } |
| 299 | + ` |
| 300 | + ) |
| 301 | + |
| 302 | + // We explicitly listed `node_modules` in the glob, so we shouldn't see a |
| 303 | + // warning. |
| 304 | + expect(result.stderr).toEqual('') |
| 305 | + |
| 306 | + // Write a file that resolves inside `node_modules` but is not covered by the |
| 307 | + // explicit glob patterns. |
| 308 | + await writeInputFile( |
| 309 | + 'node_modules/very-very-bad.html', |
| 310 | + String.raw`<div class="content-['node\_modules/very-very-bad.html']">Bad</div>` |
| 311 | + ) |
| 312 | + |
| 313 | + result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 314 | + |
| 315 | + // We still expect the node_modules file to be processed |
| 316 | + expect(result.css).toIncludeCss( |
| 317 | + css` |
| 318 | + .content-\[\'node\\_modules\/very-very-bad\.html\'\] { |
| 319 | + --tw-content: 'node_modules/very-very-bad.html'; |
| 320 | + content: var(--tw-content); |
| 321 | + } |
| 322 | + ` |
| 323 | + ) |
| 324 | + |
| 325 | + // The very-very-bad.html file is not covered by the explicit glob patterns, |
| 326 | + // so we should see a warning. |
| 327 | + expect(stripVTControlCharacters(result.stderr)).toMatchInlineSnapshot(` |
| 328 | + " |
| 329 | + warn - Your \`content\` configuration includes a pattern which looks like it's accidentally matching all of \`node_modules\` and can cause serious performance issues. |
| 330 | + warn - Pattern: \`./**/*.html\` |
| 331 | + warn - See our documentation for recommendations: |
| 332 | + warn - https://tailwindcss.com/docs/content-configuration#pattern-recommendations |
| 333 | + " |
| 334 | + `) |
| 335 | +}) |
| 336 | + |
| 337 | +it('should not warn when a negative glob is used', async () => { |
| 338 | + await writeConfigs({ |
| 339 | + both: { |
| 340 | + content: { |
| 341 | + files: ['./**/*.html', '!./node_modules/**/*.html'], |
| 342 | + }, |
| 343 | + }, |
| 344 | + }) |
| 345 | + |
| 346 | + // Write a file that resolves inside `node_modules` |
| 347 | + await writeInputFile( |
| 348 | + 'node_modules/bad.html', |
| 349 | + String.raw`<div class="content-['node\_modules/bad.html']">Bad</div>` |
| 350 | + ) |
| 351 | + |
| 352 | + let result = await build({ cwd: path.resolve(__dirname, '..') }) |
| 353 | + |
| 354 | + // The initial glob resolving shouldn't use the node_modules file |
| 355 | + // in the first place. |
| 356 | + |
| 357 | + // We still expect the node_modules file to be processed |
| 358 | + expect(result.css).not.toIncludeCss( |
| 359 | + css` |
| 360 | + .content-\[\'node\\_modules\/bad\.html\'\] { |
| 361 | + --tw-content: 'node_modules/bad.html'; |
| 362 | + content: var(--tw-content); |
| 363 | + } |
| 364 | + ` |
| 365 | + ) |
| 366 | + |
| 367 | + // The node_modules file shouldn't have been processed at all because it was |
| 368 | + // ignored by the negative glob. |
| 369 | + expect(result.stderr).toEqual('') |
| 370 | +}) |
| 371 | + |
163 | 372 | it('it handles ignored globs correctly when relative to the config', async () => {
|
164 | 373 | await writeConfigs({
|
165 | 374 | both: {
|
|
0 commit comments