tailwindcss/integrations/vite/vue.test.ts

147 lines
3.7 KiB
TypeScript
Raw Permalink Normal View History

Improve error messages when `@apply` fails (#18059) This PR improves error messages when `@apply` fails. Right now it gives you a generic error message that you cannot apply a certain utility. ```css .foo { @apply bg-red-500; } ``` Would result in: ``` Cannot apply unknown utility class: bg-red-500 ``` However, there are some situations where we can give you more context about what's happening. ### Missing `@import "tailwindcss"` or `@reference` If you are in a Vue file for example, and you have the following code: ```vue <template> <div class="foo"></div> </template> <style> .foo { @apply bg-red-500; } </style> ``` Then this will now result in: ``` Cannot apply unknown utility class `bg-white`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive ``` We do this by checking if we found a `@tailwind utilities` or `@reference`. If not, we throw this more specific error. ### Explicitly excluded classes via `@source not inline('…')` Or via the legacy `blocklist` from a JS config. If you then have the following file: ```css @import "tailwindcss"; @source not inline('bg-white'); .foo { @apply bg-white; } ``` Then this will now result in: ``` Cannot apply utility class `bg-white` because it has been explicitly disabled: https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-excluding-classes ``` We do this by checking if the class was marked as invalid. ### Applying unprefixed class in prefix mode If you have the prefix option configured, but you are applying a non-prefixed class, then we will show the following error: Given this input: ```css @import "tailwindcss" prefix(tw); .foo { @apply underline; } ``` The following error is thrown: ``` Cannot apply unprefixed utility class `underline`. Did you mean `tw:underline`? ``` ### Applying known utilities with unknown variants If you have unknown variants, then we will list them as well if the base utility does compile correctly. Given this input: ```css @import "tailwindcss"; .foo { @apply hocus:hover:pocus:bg-red-500; } ``` The following error is thrown: ``` Cannot apply utility class `hocus:hover:pocus:bg-red-500` because the `hocus` and `pocus` variants do not exist. ``` ## Test plan 1. Everything behaves the same, but the error messages give more details. 2. Updated tests with new error messages 3. Added new unit tests to verify the various scenarios 4. Added a Vue specific integration test with a `<style>…</style>` block using `@apply` [ci-all] There are some newlines here and there, let's verify that they work identically on all platforms. --------- Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2025-05-27 20:19:14 +02:00
import { stripVTControlCharacters } from 'node:util'
import { candidate, html, json, test, ts } from '../utils'
test(
'production build',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"vue": "^3.4.37",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.2",
"@tailwindcss/vite": "workspace:^",
"vite": "^7"
}
}
`,
'vite.config.ts': ts`
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [vue(), tailwindcss()],
})
`,
'index.html': html`
<!doctype html>
<html>
<body>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
`,
'src/main.ts': ts`
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
`,
'src/App.vue': html`
<style>
@import 'tailwindcss';
.foo {
@apply text-red-500;
}
</style>
Apply non-Tailwind CSS transforms in Vite plugin (#14871) Fixes: #14839 Fixes: #14796 This PR fixes an issue in the Vite extension where we previously only ran a small list of allow-listed plugins for the second stage transform in the build step. This caused some CSS features to unexpectedly not work in production builds (one such example is Vue's `:deep(...)` selector). To fix this, I changed the allow listed plugins that we do want to run to a block list to filter out some plugins we know we don't want to run (e.g. the Tailwind Vite plugin for example or some built-in Vite plugins that are not necessary). ## Test plan This PR adds a new integration test suite to test interop with a custom Vite transformer that looks like this: ```js { name: 'recolor', transform(code, id) { if (id.includes('.css')) { return code.replace(/red/g, 'blue') } }, } ``` I also validated that this does indeed fix the Vue `:deep(...)` selector related issue that we were seeing by copying the repro of #14839 into our playground: ![Screenshot 2024-11-05 at 13.35.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0Y77ilPI2WoJfMLFiAEw/4e46ab61-4acf-461a-9e40-f7c9ec3c69b2.png) You can see in the screenshot above that the `:deep()` selector overwrites the scoped styles as expected in both the dev mode and the prod build (screenshotted). Furthermore I reproduced the issue reported in https://github.com/tailwindlabs/tailwindcss/issues/14796 and was able to confirm that in a production build, the styling works as expected: <img width="517" alt="Screenshot 2024-11-06 at 14 26 50" src="https://github.com/user-attachments/assets/ade6fe38-be0d-4bd0-9a9a-67b6fec05ae0"> Lastly, I created a repository out of the biggest known-to-me Vite projects: [Astro, Nuxt, Remix, SolidStart, and SvelteKit](https://github.com/philipp-spiess/tailwind-playgrounds) and verified that both dev and prod builds show no issue and the candidate list is properly appended in each case. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-07 16:26:18 +01:00
<style scoped>
@import 'tailwindcss' reference;
Apply non-Tailwind CSS transforms in Vite plugin (#14871) Fixes: #14839 Fixes: #14796 This PR fixes an issue in the Vite extension where we previously only ran a small list of allow-listed plugins for the second stage transform in the build step. This caused some CSS features to unexpectedly not work in production builds (one such example is Vue's `:deep(...)` selector). To fix this, I changed the allow listed plugins that we do want to run to a block list to filter out some plugins we know we don't want to run (e.g. the Tailwind Vite plugin for example or some built-in Vite plugins that are not necessary). ## Test plan This PR adds a new integration test suite to test interop with a custom Vite transformer that looks like this: ```js { name: 'recolor', transform(code, id) { if (id.includes('.css')) { return code.replace(/red/g, 'blue') } }, } ``` I also validated that this does indeed fix the Vue `:deep(...)` selector related issue that we were seeing by copying the repro of #14839 into our playground: ![Screenshot 2024-11-05 at 13.35.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0Y77ilPI2WoJfMLFiAEw/4e46ab61-4acf-461a-9e40-f7c9ec3c69b2.png) You can see in the screenshot above that the `:deep()` selector overwrites the scoped styles as expected in both the dev mode and the prod build (screenshotted). Furthermore I reproduced the issue reported in https://github.com/tailwindlabs/tailwindcss/issues/14796 and was able to confirm that in a production build, the styling works as expected: <img width="517" alt="Screenshot 2024-11-06 at 14 26 50" src="https://github.com/user-attachments/assets/ade6fe38-be0d-4bd0-9a9a-67b6fec05ae0"> Lastly, I created a repository out of the biggest known-to-me Vite projects: [Astro, Nuxt, Remix, SolidStart, and SvelteKit](https://github.com/philipp-spiess/tailwind-playgrounds) and verified that both dev and prod builds show no issue and the candidate list is properly appended in each case. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-07 16:26:18 +01:00
:deep(.bar) {
color: red;
}
</style>
<template>
Apply non-Tailwind CSS transforms in Vite plugin (#14871) Fixes: #14839 Fixes: #14796 This PR fixes an issue in the Vite extension where we previously only ran a small list of allow-listed plugins for the second stage transform in the build step. This caused some CSS features to unexpectedly not work in production builds (one such example is Vue's `:deep(...)` selector). To fix this, I changed the allow listed plugins that we do want to run to a block list to filter out some plugins we know we don't want to run (e.g. the Tailwind Vite plugin for example or some built-in Vite plugins that are not necessary). ## Test plan This PR adds a new integration test suite to test interop with a custom Vite transformer that looks like this: ```js { name: 'recolor', transform(code, id) { if (id.includes('.css')) { return code.replace(/red/g, 'blue') } }, } ``` I also validated that this does indeed fix the Vue `:deep(...)` selector related issue that we were seeing by copying the repro of #14839 into our playground: ![Screenshot 2024-11-05 at 13.35.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0Y77ilPI2WoJfMLFiAEw/4e46ab61-4acf-461a-9e40-f7c9ec3c69b2.png) You can see in the screenshot above that the `:deep()` selector overwrites the scoped styles as expected in both the dev mode and the prod build (screenshotted). Furthermore I reproduced the issue reported in https://github.com/tailwindlabs/tailwindcss/issues/14796 and was able to confirm that in a production build, the styling works as expected: <img width="517" alt="Screenshot 2024-11-06 at 14 26 50" src="https://github.com/user-attachments/assets/ade6fe38-be0d-4bd0-9a9a-67b6fec05ae0"> Lastly, I created a repository out of the biggest known-to-me Vite projects: [Astro, Nuxt, Remix, SolidStart, and SvelteKit](https://github.com/philipp-spiess/tailwind-playgrounds) and verified that both dev and prod builds show no issue and the candidate list is properly appended in each case. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-07 16:26:18 +01:00
<div class="underline foo bar">Hello Vue!</div>
</template>
`,
},
},
Improve integration tests (stability + performance) (#15125) This PR improves the integration tests in two ways: 1. Make the integration tests more reliable and thus less flakey 2. Make the integration tests faster (by introducing concurrency) Tried a lot of different things to make sure that these tests are fast and stable. --- The biggest issue we noticed is that some tests are flakey, these are tests with long running dev-mode processes where watchers are being used and/or dev servers are created. To solve this, all the tests that spawn a process look at stdout/stderr and wait for a message from the process to know whether we can start making changes. For example, in case of an Astro project, you get a `watching for file changes` message. In case of Nuxt project you can wait for an `server warmed up in` and in case of Next.js there is a `Ready in` message. These depend on the tools being used, so this is hardcoded per test instead of a magically automatic solution. These messages allow us to wait until all the initial necessary work, internal watchers and/or dev servers are setup before we start making changes to the files and/or request CSS stylesheets before the server(s) are ready. --- Another improvement is how we setup the dev servers. Before, we used to try and get a free port on the system and use a `--port` flag or a `PORT` environment variable. Instead of doing this (which is slow), we rely on the process itself to show a URL with a port. Basically all tools will try to find a free port if the default port is in use. We can then use the stdout/stderr messages to get the URL and the port to use. To reduce the amount of potential conflicts in ports, we used to run every test and every file sequentially to basically guarantee that ports are free. With this new approach where we rely on the process, I noticed that we don't really run into this issue again (I reran the tests multiple times and they were always stable) <img width="316" alt="image" src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2" /> Note: these tests run Linux, Windows and macOS in this branch just for testing purposes. Once this is done, we will only run Linux tests on PRs and run all 3 of them on the `next` branch. We do make the tests concurrent by default now, which in theory means that there could be conflicts (which in practice means that the process has to do a few more tries to find a free port). To reduce these conflicts, we split up the integration tests such that Vite, PostCSS, CLI, … tests all run in a separate job in the GitHub actions workflow. <img width="312" alt="image" src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766" /> Comparing this branch against the `next` branch, this is what CI looks like right now: | `next` | `feat/improve-integration-tests` | | --- | --- | | <img width="594" alt="image" src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635" /> | <img width="672" alt="image" src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7" /> | There also was a point in time where I introduced sequential tests such that all spawned processes still run after each other, but so far I didn't run into issues if we keep them concurrent so I dropped that code. Some small changes I made to make things more reliable: 1. When relying on stdout/stderr messages, we split lines on `\n` and we strip all the ANSI escapes which allows us to not worry about special ANSI characters when finding the URL or a specific message to wait for. 2. Once a test is done, we `child.kill()` the spawned process. If that doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to force kill the process. This could technically lead to some memory or files not being cleaned up properly, but once CI is done, everything is thrown away anyway. 3. As you can see in the screenshots, I used some nicer names for the workflows. | `next` | `feat/improve-integration-tests` | | --- | --- | | <img width="276" alt="image" src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9" /> | <img width="179" alt="image" src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad" /> | They also look a bit nicer in the PR overview as well: <img width="929" alt="image" src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef" /> The very last commit just filters out Windows and macOS tests again for PRs (but they are executed on the `next` branch. --- ### Nest steps I think for now we are in a pretty good state, but there are some things we can do to further improve everything (mainly make things faster) but aren't necessary. I also ran into issue while trying it so there is more work to do. 1. More splits — instead of having a Vite folder and PostCSS folder, we can go a step further and have folders for Next.js, Astro, Nuxt, Remix, … 2. Caching — right now we have to run the build step for every OS on every "job". We can re-use the work here by introducing a setup job that the other jobs rely on. @thecrypticace and I tried it already, but were running into some Bun specific Standalone CLI issues when doing that. 3. Remote caching — we could re-enable remote caching such that the `build` step can be full turbo (e.g.: after a PR is merged in `next` and we run everything again)
2024-12-12 13:48:56 +01:00
async ({ fs, exec, expect }) => {
await exec('pnpm vite build')
let files = await fs.glob('dist/**/*.css')
expect(files).toHaveLength(1)
await fs.expectFileToContain(files[0][0], [candidate`underline`, candidate`foo`])
Apply non-Tailwind CSS transforms in Vite plugin (#14871) Fixes: #14839 Fixes: #14796 This PR fixes an issue in the Vite extension where we previously only ran a small list of allow-listed plugins for the second stage transform in the build step. This caused some CSS features to unexpectedly not work in production builds (one such example is Vue's `:deep(...)` selector). To fix this, I changed the allow listed plugins that we do want to run to a block list to filter out some plugins we know we don't want to run (e.g. the Tailwind Vite plugin for example or some built-in Vite plugins that are not necessary). ## Test plan This PR adds a new integration test suite to test interop with a custom Vite transformer that looks like this: ```js { name: 'recolor', transform(code, id) { if (id.includes('.css')) { return code.replace(/red/g, 'blue') } }, } ``` I also validated that this does indeed fix the Vue `:deep(...)` selector related issue that we were seeing by copying the repro of #14839 into our playground: ![Screenshot 2024-11-05 at 13.35.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0Y77ilPI2WoJfMLFiAEw/4e46ab61-4acf-461a-9e40-f7c9ec3c69b2.png) You can see in the screenshot above that the `:deep()` selector overwrites the scoped styles as expected in both the dev mode and the prod build (screenshotted). Furthermore I reproduced the issue reported in https://github.com/tailwindlabs/tailwindcss/issues/14796 and was able to confirm that in a production build, the styling works as expected: <img width="517" alt="Screenshot 2024-11-06 at 14 26 50" src="https://github.com/user-attachments/assets/ade6fe38-be0d-4bd0-9a9a-67b6fec05ae0"> Lastly, I created a repository out of the biggest known-to-me Vite projects: [Astro, Nuxt, Remix, SolidStart, and SvelteKit](https://github.com/philipp-spiess/tailwind-playgrounds) and verified that both dev and prod builds show no issue and the candidate list is properly appended in each case. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-07 16:26:18 +01:00
await fs.expectFileToContain(files[0][0], ['.bar{'])
},
)
Improve error messages when `@apply` fails (#18059) This PR improves error messages when `@apply` fails. Right now it gives you a generic error message that you cannot apply a certain utility. ```css .foo { @apply bg-red-500; } ``` Would result in: ``` Cannot apply unknown utility class: bg-red-500 ``` However, there are some situations where we can give you more context about what's happening. ### Missing `@import "tailwindcss"` or `@reference` If you are in a Vue file for example, and you have the following code: ```vue <template> <div class="foo"></div> </template> <style> .foo { @apply bg-red-500; } </style> ``` Then this will now result in: ``` Cannot apply unknown utility class `bg-white`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive ``` We do this by checking if we found a `@tailwind utilities` or `@reference`. If not, we throw this more specific error. ### Explicitly excluded classes via `@source not inline('…')` Or via the legacy `blocklist` from a JS config. If you then have the following file: ```css @import "tailwindcss"; @source not inline('bg-white'); .foo { @apply bg-white; } ``` Then this will now result in: ``` Cannot apply utility class `bg-white` because it has been explicitly disabled: https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-excluding-classes ``` We do this by checking if the class was marked as invalid. ### Applying unprefixed class in prefix mode If you have the prefix option configured, but you are applying a non-prefixed class, then we will show the following error: Given this input: ```css @import "tailwindcss" prefix(tw); .foo { @apply underline; } ``` The following error is thrown: ``` Cannot apply unprefixed utility class `underline`. Did you mean `tw:underline`? ``` ### Applying known utilities with unknown variants If you have unknown variants, then we will list them as well if the base utility does compile correctly. Given this input: ```css @import "tailwindcss"; .foo { @apply hocus:hover:pocus:bg-red-500; } ``` The following error is thrown: ``` Cannot apply utility class `hocus:hover:pocus:bg-red-500` because the `hocus` and `pocus` variants do not exist. ``` ## Test plan 1. Everything behaves the same, but the error messages give more details. 2. Updated tests with new error messages 3. Added new unit tests to verify the various scenarios 4. Added a Vue specific integration test with a `<style>…</style>` block using `@apply` [ci-all] There are some newlines here and there, let's verify that they work identically on all platforms. --------- Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2025-05-27 20:19:14 +02:00
test(
'error when using `@apply` without `@reference`',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"vue": "^3.4.37",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.2",
"@tailwindcss/vite": "workspace:^",
"vite": "^7"
Improve error messages when `@apply` fails (#18059) This PR improves error messages when `@apply` fails. Right now it gives you a generic error message that you cannot apply a certain utility. ```css .foo { @apply bg-red-500; } ``` Would result in: ``` Cannot apply unknown utility class: bg-red-500 ``` However, there are some situations where we can give you more context about what's happening. ### Missing `@import "tailwindcss"` or `@reference` If you are in a Vue file for example, and you have the following code: ```vue <template> <div class="foo"></div> </template> <style> .foo { @apply bg-red-500; } </style> ``` Then this will now result in: ``` Cannot apply unknown utility class `bg-white`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive ``` We do this by checking if we found a `@tailwind utilities` or `@reference`. If not, we throw this more specific error. ### Explicitly excluded classes via `@source not inline('…')` Or via the legacy `blocklist` from a JS config. If you then have the following file: ```css @import "tailwindcss"; @source not inline('bg-white'); .foo { @apply bg-white; } ``` Then this will now result in: ``` Cannot apply utility class `bg-white` because it has been explicitly disabled: https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-excluding-classes ``` We do this by checking if the class was marked as invalid. ### Applying unprefixed class in prefix mode If you have the prefix option configured, but you are applying a non-prefixed class, then we will show the following error: Given this input: ```css @import "tailwindcss" prefix(tw); .foo { @apply underline; } ``` The following error is thrown: ``` Cannot apply unprefixed utility class `underline`. Did you mean `tw:underline`? ``` ### Applying known utilities with unknown variants If you have unknown variants, then we will list them as well if the base utility does compile correctly. Given this input: ```css @import "tailwindcss"; .foo { @apply hocus:hover:pocus:bg-red-500; } ``` The following error is thrown: ``` Cannot apply utility class `hocus:hover:pocus:bg-red-500` because the `hocus` and `pocus` variants do not exist. ``` ## Test plan 1. Everything behaves the same, but the error messages give more details. 2. Updated tests with new error messages 3. Added new unit tests to verify the various scenarios 4. Added a Vue specific integration test with a `<style>…</style>` block using `@apply` [ci-all] There are some newlines here and there, let's verify that they work identically on all platforms. --------- Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2025-05-27 20:19:14 +02:00
}
}
`,
'vite.config.ts': ts`
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [vue(), tailwindcss()],
})
`,
'index.html': html`
<!doctype html>
<html>
<body>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
`,
'src/main.ts': ts`
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
`,
'src/App.vue': html`
<template>
<div class="foo">Hello Vue!</div>
</template>
<style>
.foo {
@apply text-red-500;
}
</style>
`,
},
},
async ({ exec, expect }) => {
expect.assertions(1)
try {
await exec('pnpm vite build')
} catch (error) {
let [, message] =
/error during build:([\s\S]*?)file:/g.exec(
stripVTControlCharacters(error.message.replace(/\r?\n/g, '\n')),
) ?? []
expect(message.trim()).toMatchInlineSnapshot(
`"[@tailwindcss/vite:generate:build] Cannot apply unknown utility class \`text-red-500\`. Are you using CSS modules or similar and missing \`@reference\`? https://tailwindcss.com/docs/functions-and-directives#reference-directive"`,
)
}
},
)