tailwindcss/integrations/utils.ts

606 lines
20 KiB
TypeScript
Raw Normal View History

Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
import dedent from 'dedent'
import fastGlob from 'fast-glob'
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
import { exec, spawn } from 'node:child_process'
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
import fs from 'node:fs/promises'
import { platform, tmpdir } from 'node:os'
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
import path from 'node:path'
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
import { stripVTControlCharacters } from 'node:util'
import { test as defaultTest, type ExpectStatic } from 'vitest'
import { escape } from '../packages/tailwindcss/src/utils/escape'
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
const REPO_ROOT = path.join(__dirname, '..')
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
const PUBLIC_PACKAGES = (await fs.readdir(path.join(REPO_ROOT, 'dist'))).map((name) =>
name.replace('tailwindcss-', '@tailwindcss/').replace('.tgz', ''),
)
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
interface SpawnedProcess {
dispose: () => void
flush: () => void
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
onStdout: (predicate: (message: string) => boolean) => Promise<void>
onStderr: (predicate: (message: string) => boolean) => Promise<void>
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
interface ChildProcessOptions {
cwd?: string
env?: Record<string, string>
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
}
Throw a useful error when `tailwindcss` is used as a PostCSS plugin (#14378) While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
interface ExecOptions {
ignoreStdErr?: boolean
stdin?: string
Throw a useful error when `tailwindcss` is used as a PostCSS plugin (#14378) While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
interface TestConfig {
fs: {
[filePath: string]: string | Uint8Array
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
installDependencies?: boolean
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
interface TestContext {
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
root: string
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
expect: ExpectStatic
Throw a useful error when `tailwindcss` is used as a PostCSS plugin (#14378) While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
exec(command: string, options?: ChildProcessOptions, execOptions?: ExecOptions): Promise<string>
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
spawn(command: string, options?: ChildProcessOptions): Promise<SpawnedProcess>
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
fs: {
write(filePath: string, content: string, encoding?: BufferEncoding): Promise<void>
create(filePaths: string[]): Promise<void>
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
read(filePath: string): Promise<string>
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
glob(pattern: string): Promise<[string, string][]>
dumpFiles(pattern: string): Promise<string>
expectFileToContain(
filePath: string,
Vite: Don't track candidate changes for Svelte `<style>` tags (#14981) Closes #14965 This PR changes the way we register Tailwind CSS as a Svelte preprocessor when using the Vite plugin. The idea is to reduce the bookkeeping for interacting with CSS inside `<style>` tags so that we have a more consistent behavior and make sure the Svelte-specific post-processing (e.g. local class mangling) works as expected. Prior to this change, we were running Tailwind CSS as a Svelte preprocessor and then we would transform the file again when necessary inside the Vite `transform` hook. This is necessary to have the right list of candidates when we build the final CSS, but it did cause some situation to not apply the Svelte post-processors anymore. The repro for this seemed to indicate a timing specific issue and I did notice that specifically the code where we invalidate modules in Vite would cause unexpected processing orders. We do, however, not officially support rendering utilities (`@tailwind utilities;`) inside `<style>` tag. This is because the `<style>` block is scoped by default and emitting utilities will always include utilities for all classes in your whole project. For this case, we highly recommend creating as separate `.css` file and importing it explicitly. With this limitation in place, the additional bookkeeping where we need to invalidate modules because the candidate list has changed is no longer necessary and removing it allows us to reduce the complexity of the Svelte integration. ## Test Plan https://github.com/user-attachments/assets/32c8e91f-ab21-48c6-aeaf-2582273b9bac Not seen in the test plan above I also tested the `pnpm build --watch` step of the Vite project. This does require the `pnpm preview` server to restart but the build artifact are updated as expected.
2024-11-13 16:42:49 +01:00
contents: string | RegExp | (string | RegExp)[],
): Promise<void>
Improve compatibility with `@tailwindcss/typography` and `@tailwindcss/forms` (#14221) This PR enables compatibility for the `@tailwindcss/typography` and `@tailwindcss/forms` plugins. This required the addition of new Plugin APIs and new package exports. ## New Plugin APIs and compatibility improvements We added support for `addComponents`, `matchComponents`, and `prefix`. The component APIs are an alias for the utilities APIs because the sorting in V4 is different and emitting components in a custom `@layer` is not necessary. Since `prefix` is not supported in V4, the `prefix()` API is currently an identity function. ```js addComponents({ '.btn': { padding: '.5rem 1rem', borderRadius: '.25rem', fontWeight: '600', }, '.btn-blue': { backgroundColor: '#3490dc', color: '#fff', '&:hover': { backgroundColor: '#2779bd', }, }, '.btn-red': { backgroundColor: '#e3342f', color: '#fff', '&:hover': { backgroundColor: '#cc1f1a', }, }, }) ``` The behavioral changes effect the `addUtilities` and `matchUtilities` functions, we now: - Allow arrays of CSS property objects to be emitted: ```js addUtilities({ '.text-trim': [ {'text-box-trim': 'both'}, {'text-box-edge': 'cap alphabetic'}, ], }) ``` - Allow arrays of utilities ```js addUtilities([ { '.text-trim':{ 'text-box-trim': 'both', 'text-box-edge': 'cap alphabetic', }, } ]) ``` - Allow more complicated selector names ```js addUtilities({ '.form-input, .form-select, .form-radio': { /* styles here */ }, '.form-input::placeholder': { /* styles here */ }, '.form-checkbox:indeterminate:checked': { /* styles here */ } }) ``` ## New `tailwindcss/color` and `tailwindcss/defaultTheme` export To be compatible to v3, we're adding two new exports to the tailwindcss package. These match the default theme values as defined in v3: ```ts import colors from 'tailwindcss/colors' console.log(colors.red[600]) ``` ```ts import theme from 'tailwindcss/defaultTheme' console.log(theme.spacing[4]) ``` --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 08:06:21 -04:00
expectFileNotToContain(filePath: string, contents: string | string[]): Promise<void>
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
}
type TestCallback = (context: TestContext) => Promise<void> | void
interface TestFlags {
only?: boolean
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
skip?: boolean
debug?: boolean
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
type SpawnActor = { predicate: (message: string) => boolean; resolve: () => void }
export const IS_WINDOWS = platform() === 'win32'
const TEST_TIMEOUT = IS_WINDOWS ? 120000 : 60000
const ASSERTION_TIMEOUT = IS_WINDOWS ? 10000 : 5000
// On Windows CI, tmpdir returns a path containing a weird RUNNER~1 folder that
// apparently causes the vite builds to not work.
const TMP_ROOT =
process.env.CI && IS_WINDOWS ? path.dirname(process.env.GITHUB_WORKSPACE!) : tmpdir()
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
export function test(
name: string,
config: TestConfig,
testCallback: TestCallback,
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
{ only = false, skip = false, debug = false }: TestFlags = {},
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
) {
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
return defaultTest(
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
name,
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
{
timeout: TEST_TIMEOUT,
retry: process.env.CI ? 2 : 0,
only: only || (!process.env.CI && debug),
skip,
concurrent: true,
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
async (options) => {
let rootDir = debug ? path.join(REPO_ROOT, '.debug') : TMP_ROOT
await fs.mkdir(rootDir, { recursive: true })
let root = await fs.mkdtemp(path.join(rootDir, 'tailwind-integrations'))
if (debug) {
console.log('Running test in debug mode. File system will be written to:')
console.log(root)
console.log()
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let context = {
root,
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
expect: options.expect,
Throw a useful error when `tailwindcss` is used as a PostCSS plugin (#14378) While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
async exec(
command: string,
childProcessOptions: ChildProcessOptions = {},
execOptions: ExecOptions = {},
) {
let cwd = childProcessOptions.cwd ?? root
if (debug && cwd !== root) {
let relative = path.relative(root, cwd)
if (relative[0] !== '.') relative = `./${relative}`
console.log(`> cd ${relative}`)
}
if (debug) console.log(`> ${command}`)
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
return new Promise((resolve, reject) => {
let child = exec(
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
command,
{
cwd,
...childProcessOptions,
env: childProcessOptions.env,
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
},
(error, stdout, stderr) => {
if (error) {
Throw a useful error when `tailwindcss` is used as a PostCSS plugin (#14378) While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
if (execOptions.ignoreStdErr !== true) console.error(stderr)
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (only || debug) {
console.error(stdout)
}
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
reject(error)
} else {
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (only || debug) {
console.log(stdout.toString() + '\n\n' + stderr.toString())
}
resolve(stdout.toString() + '\n\n' + stderr.toString())
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
}
},
)
if (execOptions.stdin) {
child.stdin?.write(execOptions.stdin)
child.stdin?.end()
}
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
})
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
},
async spawn(command: string, childProcessOptions: ChildProcessOptions = {}) {
let resolveDisposal: (() => void) | undefined
let rejectDisposal: ((error: Error) => void) | undefined
let disposePromise = new Promise<void>((resolve, reject) => {
resolveDisposal = resolve
rejectDisposal = reject
})
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
let cwd = childProcessOptions.cwd ?? root
if (debug && cwd !== root) {
let relative = path.relative(root, cwd)
if (relative[0] !== '.') relative = `./${relative}`
console.log(`> cd ${relative}`)
}
if (debug) console.log(`>& ${command}`)
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let child = spawn(command, {
cwd,
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
shell: true,
...childProcessOptions,
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
env: {
...process.env,
...childProcessOptions.env,
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
},
})
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
function dispose() {
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
if (!child.kill()) {
child.kill('SIGKILL')
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let timer = setTimeout(
() =>
rejectDisposal?.(new Error(`spawned process (${command}) did not exit in time`)),
ASSERTION_TIMEOUT,
)
disposePromise.finally(() => {
clearTimeout(timer)
})
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
return disposePromise
}
disposables.push(dispose)
function onExit() {
resolveDisposal?.()
}
let stdoutMessages: string[] = []
let stderrMessages: string[] = []
let stdoutActors: SpawnActor[] = []
let stderrActors: SpawnActor[] = []
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
function notifyNext(actors: SpawnActor[], messages: string[]) {
if (actors.length <= 0) return
let [next] = actors
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
for (let [idx, message] of messages.entries()) {
if (next.predicate(message)) {
messages.splice(0, idx + 1)
let actorIdx = actors.indexOf(next)
actors.splice(actorIdx, 1)
next.resolve()
break
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let combined: ['stdout' | 'stderr', string][] = []
child.stdout.on('data', (result) => {
let content = result.toString()
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (debug || only) console.log(content)
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
combined.push(['stdout', content])
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
for (let line of content.split('\n')) {
stdoutMessages.push(stripVTControlCharacters(line))
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
notifyNext(stdoutActors, stdoutMessages)
})
child.stderr.on('data', (result) => {
let content = result.toString()
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (debug || only) console.error(content)
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
combined.push(['stderr', content])
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
for (let line of content.split('\n')) {
stderrMessages.push(stripVTControlCharacters(line))
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
notifyNext(stderrActors, stderrMessages)
})
child.on('exit', onExit)
child.on('error', (error) => {
if (error.name !== 'AbortError') {
throw error
}
})
options.onTestFailed(() => {
2024-11-04 13:07:25 -05:00
// In only or debug mode, messages are logged to the console
// immediately.
if (only || debug) return
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
for (let [type, message] of combined) {
if (type === 'stdout') {
console.log(message)
} else {
console.error(message)
}
}
})
return {
dispose,
flush() {
stdoutActors.splice(0)
stderrActors.splice(0)
stdoutMessages.splice(0)
stderrMessages.splice(0)
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
onStdout(predicate: (message: string) => boolean) {
return new Promise<void>((resolve) => {
stdoutActors.push({ predicate, resolve })
notifyNext(stdoutActors, stdoutMessages)
})
},
onStderr(predicate: (message: string) => boolean) {
return new Promise<void>((resolve) => {
stderrActors.push({ predicate, resolve })
notifyNext(stderrActors, stderrMessages)
})
},
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
},
fs: {
async write(
filename: string,
content: string | Uint8Array,
encoding: BufferEncoding = 'utf8',
): Promise<void> {
let full = path.join(root, filename)
let dir = path.dirname(full)
await fs.mkdir(dir, { recursive: true })
if (typeof content !== 'string') {
return await fs.writeFile(full, content)
}
if (filename.endsWith('package.json')) {
content = await overwriteVersionsInPackageJson(content)
}
// Ensure that files written on Windows use \r\n line ending
if (IS_WINDOWS) {
content = content.replace(/\n/g, '\r\n')
}
await fs.writeFile(full, content, encoding)
},
async create(filenames: string[]): Promise<void> {
for (let filename of filenames) {
let full = path.join(root, filename)
let dir = path.dirname(full)
await fs.mkdir(dir, { recursive: true })
await fs.writeFile(full, '')
}
},
Vite: Retain candidates between input CSS updates (#14228) This PR fixes an issue introduced with the changed candidate cache behavior in #14187. Prior to #14187, candidates were cached globally within an instance of Oxide. This meant that once a candidate was discovered, it would not reset until you either manually cleared the cache or restarted the Oxide process. With the changes in #14187 however, the cache was scoped to the instance of the `Scanner` class with the intention of making the caching behavior more easy to understand and to avoid a global cache. This, however, had an unforeseen side-effect in our Vite extension. Vite, in dev mode, discovers files _lazily_. So when a developer goes to `/index.html` the first time, we will scan the `/index.html` file for Tailwind candidates and then build a CSS file with those candidate. When they go to `/about.html` later, we will _append_ the candidates from the new file and so forth. The problem now arises when the dev server detects changes to the input CSS file. This requires us to do a re-scan of that CSS file which, after #14187, caused the candidate cache to be gone. This is usually fine since we would just scan files again for the changed candidate list but in the Vite case we would only get the input CSS file change _but no subsequent change events for all other files, including those currently rendered in the browser_). This caused updates to the CSS file to remove all candidates from the CSS file again. Ideally, we can separate between two concepts: The candidate cache and the CSS input file scan. An instance of the `Scanner` could re-parse the input CSS file without having to throw away previous candidates. This, however, would have another issue with the current Vite extension where we do not properly retain instances of the `Scanner` class anyways. To properly improve the cache behavior, we will have to fix the Vite `Scanner` retaining behavior first. Unfortunately this means that for the short term, we have to add some manual bookkeeping to the Vite client and retain the candidate cache between builds ourselves. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-21 12:54:42 +02:00
async read(filePath: string) {
let content = await fs.readFile(path.resolve(root, filePath), 'utf8')
// Ensure that files read on Windows have \r\n line endings removed
if (IS_WINDOWS) {
content = content.replace(/\r\n/g, '\n')
}
return content
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
async glob(pattern: string) {
let files = await fastGlob(pattern, { cwd: root })
return Promise.all(
files.map(async (file) => {
let content = await fs.readFile(path.join(root, file), 'utf8')
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
return [
file,
// Drop license comment
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
content.replace(/[\s\n]*\/\*![\s\S]*?\*\/[\s\n]*/g, ''),
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
]
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
}),
)
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
},
async dumpFiles(pattern: string) {
let files = await context.fs.glob(pattern)
return `\n${files
.slice()
.sort((a: [string], z: [string]) => {
let aParts = a[0].split('/')
let zParts = z[0].split('/')
let aFile = aParts.at(-1)
Improve multi-root `@config` linking (#15001) This PR improves the discoverability of Tailwind config files when we are trying to link them to your CSS files. When you have multiple "root" CSS files in your project, and if they don't include an `@config` directive, then we tried to find the Tailwind config file in your current working directory. This means that if you run the upgrade command from the root of your project, and you have a nested folder with a separate Tailwind setup, then the nested CSS file would link to the root Tailwind config file. Visually, you can think of it like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (1) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` If you run the upgrade command from the root of your project, then the `/src/styles/index.css` will be linked to `/tailwind.config.js` which is what we expect. But `/admin/src/styles/index.css` will _also_ be linked to `/tailwind.config.js` With this PR we improve this behavior by looking at the CSS file, and crawling up the parent tree. This mens that the new behavior looks like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (2) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` Now `/src/styles/index.css` will be linked to `/tailwind.config.js`, and `/admin/src/styles/index.css` will be linked to `/admin/tailwind.config.js`. When we discover the Tailwind config file, we will also print a message to the user to let them know which CSS file is linked to which Tailwind config file. This should be a safe improvement because if your Tailwind config file had a different name, or if it lived in a sibling folder then Tailwind wouldn't find it either and you already required a `@config "…";` directive in your CSS file to point to the correct file. In the unlikely event that it turns out that 2 (or more) CSS files resolve to the same to the same Tailwind config file, then an upgrade might not be safe and some manual intervention might be needed. In this case, we will show a warning about this. <img width="1552" alt="image" src="https://github.com/user-attachments/assets/7a1ad11d-18c5-4b7d-9a02-14f0116ae955"> Test plan: --- - Added an integration test that properly links the nearest Tailwind config file by looking up the tree - Added an integration test that resolves 2 or more CSS files to the same config file, resulting in an error where manual intervention is needed - Ran it on the Tailwind UI codebase Running this on Tailwind UI's codebase it looks like this: <img width="1552" alt="image" src="https://github.com/user-attachments/assets/21785428-5e0d-47f7-80ec-dab497f58784"> --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-11-18 17:43:44 +01:00
let zFile = zParts.at(-1)
// Sort by depth, shallow first
if (aParts.length < zParts.length) return -1
if (aParts.length > zParts.length) return 1
Improve multi-root `@config` linking (#15001) This PR improves the discoverability of Tailwind config files when we are trying to link them to your CSS files. When you have multiple "root" CSS files in your project, and if they don't include an `@config` directive, then we tried to find the Tailwind config file in your current working directory. This means that if you run the upgrade command from the root of your project, and you have a nested folder with a separate Tailwind setup, then the nested CSS file would link to the root Tailwind config file. Visually, you can think of it like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (1) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` If you run the upgrade command from the root of your project, then the `/src/styles/index.css` will be linked to `/tailwind.config.js` which is what we expect. But `/admin/src/styles/index.css` will _also_ be linked to `/tailwind.config.js` With this PR we improve this behavior by looking at the CSS file, and crawling up the parent tree. This mens that the new behavior looks like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (2) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` Now `/src/styles/index.css` will be linked to `/tailwind.config.js`, and `/admin/src/styles/index.css` will be linked to `/admin/tailwind.config.js`. When we discover the Tailwind config file, we will also print a message to the user to let them know which CSS file is linked to which Tailwind config file. This should be a safe improvement because if your Tailwind config file had a different name, or if it lived in a sibling folder then Tailwind wouldn't find it either and you already required a `@config "…";` directive in your CSS file to point to the correct file. In the unlikely event that it turns out that 2 (or more) CSS files resolve to the same to the same Tailwind config file, then an upgrade might not be safe and some manual intervention might be needed. In this case, we will show a warning about this. <img width="1552" alt="image" src="https://github.com/user-attachments/assets/7a1ad11d-18c5-4b7d-9a02-14f0116ae955"> Test plan: --- - Added an integration test that properly links the nearest Tailwind config file by looking up the tree - Added an integration test that resolves 2 or more CSS files to the same config file, resulting in an error where manual intervention is needed - Ran it on the Tailwind UI codebase Running this on Tailwind UI's codebase it looks like this: <img width="1552" alt="image" src="https://github.com/user-attachments/assets/21785428-5e0d-47f7-80ec-dab497f58784"> --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-11-18 17:43:44 +01:00
// Sort by folder names, alphabetically
for (let i = 0; i < aParts.length - 1; i++) {
let diff = aParts[i].localeCompare(zParts[i])
if (diff !== 0) return diff
}
// Sort by filename, sort files named `index` before others
Improve multi-root `@config` linking (#15001) This PR improves the discoverability of Tailwind config files when we are trying to link them to your CSS files. When you have multiple "root" CSS files in your project, and if they don't include an `@config` directive, then we tried to find the Tailwind config file in your current working directory. This means that if you run the upgrade command from the root of your project, and you have a nested folder with a separate Tailwind setup, then the nested CSS file would link to the root Tailwind config file. Visually, you can think of it like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (1) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` If you run the upgrade command from the root of your project, then the `/src/styles/index.css` will be linked to `/tailwind.config.js` which is what we expect. But `/admin/src/styles/index.css` will _also_ be linked to `/tailwind.config.js` With this PR we improve this behavior by looking at the CSS file, and crawling up the parent tree. This mens that the new behavior looks like this: ``` . ├── admin │   ├── src │   │   └── styles │   │   └── index.css <-- This will be linked to (2) │   └── tailwind.config.js (2) ├── src │   └── styles │   └── index.css <-- This will be linked to (1) └── tailwind.config.js (1) ``` Now `/src/styles/index.css` will be linked to `/tailwind.config.js`, and `/admin/src/styles/index.css` will be linked to `/admin/tailwind.config.js`. When we discover the Tailwind config file, we will also print a message to the user to let them know which CSS file is linked to which Tailwind config file. This should be a safe improvement because if your Tailwind config file had a different name, or if it lived in a sibling folder then Tailwind wouldn't find it either and you already required a `@config "…";` directive in your CSS file to point to the correct file. In the unlikely event that it turns out that 2 (or more) CSS files resolve to the same to the same Tailwind config file, then an upgrade might not be safe and some manual intervention might be needed. In this case, we will show a warning about this. <img width="1552" alt="image" src="https://github.com/user-attachments/assets/7a1ad11d-18c5-4b7d-9a02-14f0116ae955"> Test plan: --- - Added an integration test that properly links the nearest Tailwind config file by looking up the tree - Added an integration test that resolves 2 or more CSS files to the same config file, resulting in an error where manual intervention is needed - Ran it on the Tailwind UI codebase Running this on Tailwind UI's codebase it looks like this: <img width="1552" alt="image" src="https://github.com/user-attachments/assets/21785428-5e0d-47f7-80ec-dab497f58784"> --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-11-18 17:43:44 +01:00
if (aFile?.startsWith('index') && !zFile?.startsWith('index')) return -1
if (zFile?.startsWith('index') && !aFile?.startsWith('index')) return 1
// Sort by filename, alphabetically
return a[0].localeCompare(z[0])
})
.map(([file, content]) => `--- ${file} ---\n${content || '<EMPTY>'}`)
.join('\n\n')
.trim()}\n`
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
async expectFileToContain(filePath, contents) {
return retryAssertion(async () => {
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let fileContent = await this.read(filePath)
for (let content of Array.isArray(contents) ? contents : [contents]) {
if (content instanceof RegExp) {
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
options.expect(fileContent).toMatch(content)
} else {
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
options.expect(fileContent).toContain(content)
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
})
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
},
Improve compatibility with `@tailwindcss/typography` and `@tailwindcss/forms` (#14221) This PR enables compatibility for the `@tailwindcss/typography` and `@tailwindcss/forms` plugins. This required the addition of new Plugin APIs and new package exports. ## New Plugin APIs and compatibility improvements We added support for `addComponents`, `matchComponents`, and `prefix`. The component APIs are an alias for the utilities APIs because the sorting in V4 is different and emitting components in a custom `@layer` is not necessary. Since `prefix` is not supported in V4, the `prefix()` API is currently an identity function. ```js addComponents({ '.btn': { padding: '.5rem 1rem', borderRadius: '.25rem', fontWeight: '600', }, '.btn-blue': { backgroundColor: '#3490dc', color: '#fff', '&:hover': { backgroundColor: '#2779bd', }, }, '.btn-red': { backgroundColor: '#e3342f', color: '#fff', '&:hover': { backgroundColor: '#cc1f1a', }, }, }) ``` The behavioral changes effect the `addUtilities` and `matchUtilities` functions, we now: - Allow arrays of CSS property objects to be emitted: ```js addUtilities({ '.text-trim': [ {'text-box-trim': 'both'}, {'text-box-edge': 'cap alphabetic'}, ], }) ``` - Allow arrays of utilities ```js addUtilities([ { '.text-trim':{ 'text-box-trim': 'both', 'text-box-edge': 'cap alphabetic', }, } ]) ``` - Allow more complicated selector names ```js addUtilities({ '.form-input, .form-select, .form-radio': { /* styles here */ }, '.form-input::placeholder': { /* styles here */ }, '.form-checkbox:indeterminate:checked': { /* styles here */ } }) ``` ## New `tailwindcss/color` and `tailwindcss/defaultTheme` export To be compatible to v3, we're adding two new exports to the tailwindcss package. These match the default theme values as defined in v3: ```ts import colors from 'tailwindcss/colors' console.log(colors.red[600]) ``` ```ts import theme from 'tailwindcss/defaultTheme' console.log(theme.spacing[4]) ``` --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 08:06:21 -04:00
async expectFileNotToContain(filePath, contents) {
return retryAssertion(async () => {
let fileContent = await this.read(filePath)
for (let content of contents) {
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
options.expect(fileContent).not.toContain(content)
Improve compatibility with `@tailwindcss/typography` and `@tailwindcss/forms` (#14221) This PR enables compatibility for the `@tailwindcss/typography` and `@tailwindcss/forms` plugins. This required the addition of new Plugin APIs and new package exports. ## New Plugin APIs and compatibility improvements We added support for `addComponents`, `matchComponents`, and `prefix`. The component APIs are an alias for the utilities APIs because the sorting in V4 is different and emitting components in a custom `@layer` is not necessary. Since `prefix` is not supported in V4, the `prefix()` API is currently an identity function. ```js addComponents({ '.btn': { padding: '.5rem 1rem', borderRadius: '.25rem', fontWeight: '600', }, '.btn-blue': { backgroundColor: '#3490dc', color: '#fff', '&:hover': { backgroundColor: '#2779bd', }, }, '.btn-red': { backgroundColor: '#e3342f', color: '#fff', '&:hover': { backgroundColor: '#cc1f1a', }, }, }) ``` The behavioral changes effect the `addUtilities` and `matchUtilities` functions, we now: - Allow arrays of CSS property objects to be emitted: ```js addUtilities({ '.text-trim': [ {'text-box-trim': 'both'}, {'text-box-edge': 'cap alphabetic'}, ], }) ``` - Allow arrays of utilities ```js addUtilities([ { '.text-trim':{ 'text-box-trim': 'both', 'text-box-edge': 'cap alphabetic', }, } ]) ``` - Allow more complicated selector names ```js addUtilities({ '.form-input, .form-select, .form-radio': { /* styles here */ }, '.form-input::placeholder': { /* styles here */ }, '.form-checkbox:indeterminate:checked': { /* styles here */ } }) ``` ## New `tailwindcss/color` and `tailwindcss/defaultTheme` export To be compatible to v3, we're adding two new exports to the tailwindcss package. These match the default theme values as defined in v3: ```ts import colors from 'tailwindcss/colors' console.log(colors.red[600]) ``` ```ts import theme from 'tailwindcss/defaultTheme' console.log(theme.spacing[4]) ``` --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 08:06:21 -04:00
}
})
},
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
} satisfies TestContext
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
Improve compatibility with `@tailwindcss/typography` and `@tailwindcss/forms` (#14221) This PR enables compatibility for the `@tailwindcss/typography` and `@tailwindcss/forms` plugins. This required the addition of new Plugin APIs and new package exports. ## New Plugin APIs and compatibility improvements We added support for `addComponents`, `matchComponents`, and `prefix`. The component APIs are an alias for the utilities APIs because the sorting in V4 is different and emitting components in a custom `@layer` is not necessary. Since `prefix` is not supported in V4, the `prefix()` API is currently an identity function. ```js addComponents({ '.btn': { padding: '.5rem 1rem', borderRadius: '.25rem', fontWeight: '600', }, '.btn-blue': { backgroundColor: '#3490dc', color: '#fff', '&:hover': { backgroundColor: '#2779bd', }, }, '.btn-red': { backgroundColor: '#e3342f', color: '#fff', '&:hover': { backgroundColor: '#cc1f1a', }, }, }) ``` The behavioral changes effect the `addUtilities` and `matchUtilities` functions, we now: - Allow arrays of CSS property objects to be emitted: ```js addUtilities({ '.text-trim': [ {'text-box-trim': 'both'}, {'text-box-edge': 'cap alphabetic'}, ], }) ``` - Allow arrays of utilities ```js addUtilities([ { '.text-trim':{ 'text-box-trim': 'both', 'text-box-edge': 'cap alphabetic', }, } ]) ``` - Allow more complicated selector names ```js addUtilities({ '.form-input, .form-select, .form-radio': { /* styles here */ }, '.form-input::placeholder': { /* styles here */ }, '.form-checkbox:indeterminate:checked': { /* styles here */ } }) ``` ## New `tailwindcss/color` and `tailwindcss/defaultTheme` export To be compatible to v3, we're adding two new exports to the tailwindcss package. These match the default theme values as defined in v3: ```ts import colors from 'tailwindcss/colors' console.log(colors.red[600]) ``` ```ts import theme from 'tailwindcss/defaultTheme' console.log(theme.spacing[4]) ``` --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 08:06:21 -04:00
config.fs['.gitignore'] ??= txt`
node_modules/
`
for (let [filename, content] of Object.entries(config.fs)) {
await context.fs.write(filename, content)
}
let shouldInstallDependencies = config.installDependencies ?? true
try {
// In debug mode, the directory is going to be inside the pnpm workspace
// of the tailwindcss package. This means that `pnpm install` will run
// pnpm install on the workspace instead (expect if the root dir defines
// a separate workspace). We work around this by using the
// `--ignore-workspace` flag.
if (shouldInstallDependencies) {
let ignoreWorkspace = debug && !config.fs['pnpm-workspace.yaml']
await context.exec(`pnpm install${ignoreWorkspace ? ' --ignore-workspace' : ''}`)
}
} catch (error: any) {
console.error(error)
console.error(error.stdout?.toString())
console.error(error.stderr?.toString())
throw error
}
let disposables: (() => Promise<void>)[] = []
async function dispose() {
await Promise.all(disposables.map((dispose) => dispose()))
if (!debug) {
await gracefullyRemove(root)
}
}
options.onTestFinished(dispose)
// Make it a git repository, and commit all files
if (only || debug) {
try {
await context.exec('git init', { cwd: root })
await context.exec('git add --all', { cwd: root })
await context.exec('git commit -m "before migration"', { cwd: root })
} catch (error: any) {
console.error(error)
console.error(error.stdout?.toString())
console.error(error.stderr?.toString())
throw error
}
}
return await testCallback(context)
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
},
)
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
test.only = (name: string, config: TestConfig, testCallback: TestCallback) => {
return test(name, config, testCallback, { only: true })
}
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
test.skip = (name: string, config: TestConfig, testCallback: TestCallback) => {
return test(name, config, testCallback, { skip: true })
}
test.debug = (name: string, config: TestConfig, testCallback: TestCallback) => {
return test(name, config, testCallback, { debug: true })
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
// Maps package names to their tarball filenames. See scripts/pack-packages.ts
// for more details.
function pkgToFilename(name: string) {
return `${name.replace('@', '').replace('/', '-')}.tgz`
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
async function overwriteVersionsInPackageJson(content: string): Promise<string> {
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
let json = JSON.parse(content)
// Resolve all workspace:^ versions to local tarballs
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
for (let key of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
let dependencies = json[key] || {}
for (let dependency in dependencies) {
if (dependencies[dependency] === 'workspace:^') {
dependencies[dependency] = resolveVersion(dependency)
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
}
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
}
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
// Inject transitive dependency overwrite. This is necessary because
// @tailwindcss/vite internally depends on a specific version of
// @tailwindcss/oxide and we instead want to resolve it to the locally built
// version.
json.pnpm ||= {}
json.pnpm.overrides ||= {}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
for (let pkg of PUBLIC_PACKAGES) {
Ensure clients pin the `tailwindcss` version (#15011) We noticed that in the current alpha 34 release, the `package.json` file of the `@tailwindcss/node` package only defines `tailwindcss` as a dev dependency. This makes it very easy for version mismatches to happen when a v3 version (or an earlier v4 alpha for that matter) was installed in the same project: ```json { "name": "@tailwindcss/node", "version": "4.0.0-alpha.34", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/tailwindlabs/tailwindcss.git", "directory": "packages/@tailwindcss-node" }, "bugs": "https://github.com/tailwindlabs/tailwindcss/issues", "homepage": "https://tailwindcss.com", "files": [ "dist/" ], "publishConfig": { "provenance": true, "access": "public" }, "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" }, "./require-cache": { "types": "./dist/require-cache.d.ts", "default": "./dist/require-cache.js" }, "./esm-cache-loader": { "types": "./dist/esm-cache.loader.d.mts", "default": "./dist/esm-cache.loader.mjs" } }, "devDependencies": { "tailwindcss": "4.0.0-alpha.34" }, "dependencies": { "enhanced-resolve": "^5.17.1", "jiti": "^2.0.0-beta.3" }, "scripts": { "build": "tsup-node", "dev": "pnpm run build -- --watch" } } ``` Furthermore, we were trying to fix issues where our integration test setup could not install `tailwindcss@3` because of how we did pnpm overrides. This PR fixes this by: - Ensuring every client that calls into `tailwindcss` core marks it as a version-pinned dependency. You are still required to install `tailwindcss` in your project along side a client (e.g. `@tailwindcss/vite`) but we now only use your installed version for importing the respective `.css` files. For the core logic, we are now requiring each package to use `tailwindcss` at the same version. This should help resolve issues like https://github.com/tailwindlabs/tailwindcss/discussions/14652 - We tried to eliminate the dependency on `tailwindcss` from the `@tailwindcss/upgrade` package. Unfortunately this is not possible to do right now because we need to load the CSS files from v4 to create the right environment. In a future version we could bundle the required CSS files with `@tailwidncss/upgrade` but it doesn't seem necessary for now. - We then changed our integration test overrides to only override the `tailwindcss` package that are dependencies of the known list of packages that we have `tailwindcss` dependencies on: `@tailwindcss/node` and `@tailwindcss/upgrade`. This ensures that we can install v3 of `tailwindcss` in the integration tests and it will work. Something we want to do for some upgrade tests. # Test plan Integration work again. Furthermore we added a quick setup with the CLI using the local tarballs and ensured it works: ```bash pnpm init pnpm install ../../tailwindcss/dist/tailwindcss-cli.tgz pnpm install ../../tailwindcss/dist/tailwindcss.tgz echo '@import "tailwindcss";' > index.css echo '<div class="underline"></div>' > index.html pnpm tailwindcss -i index.css -o out.css cat out.css ```
2024-11-15 17:18:48 +01:00
if (pkg === 'tailwindcss') {
// We want to be explicit about the `tailwindcss` package so our tests can
// also import v3 without conflicting v4 tarballs.
json.pnpm.overrides['@tailwindcss/node>tailwindcss'] = resolveVersion(pkg)
json.pnpm.overrides['@tailwindcss/upgrade>tailwindcss'] = resolveVersion(pkg)
json.pnpm.overrides['@tailwindcss/cli>tailwindcss'] = resolveVersion(pkg)
json.pnpm.overrides['@tailwindcss/postcss>tailwindcss'] = resolveVersion(pkg)
json.pnpm.overrides['@tailwindcss/vite>tailwindcss'] = resolveVersion(pkg)
Ensure clients pin the `tailwindcss` version (#15011) We noticed that in the current alpha 34 release, the `package.json` file of the `@tailwindcss/node` package only defines `tailwindcss` as a dev dependency. This makes it very easy for version mismatches to happen when a v3 version (or an earlier v4 alpha for that matter) was installed in the same project: ```json { "name": "@tailwindcss/node", "version": "4.0.0-alpha.34", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/tailwindlabs/tailwindcss.git", "directory": "packages/@tailwindcss-node" }, "bugs": "https://github.com/tailwindlabs/tailwindcss/issues", "homepage": "https://tailwindcss.com", "files": [ "dist/" ], "publishConfig": { "provenance": true, "access": "public" }, "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" }, "./require-cache": { "types": "./dist/require-cache.d.ts", "default": "./dist/require-cache.js" }, "./esm-cache-loader": { "types": "./dist/esm-cache.loader.d.mts", "default": "./dist/esm-cache.loader.mjs" } }, "devDependencies": { "tailwindcss": "4.0.0-alpha.34" }, "dependencies": { "enhanced-resolve": "^5.17.1", "jiti": "^2.0.0-beta.3" }, "scripts": { "build": "tsup-node", "dev": "pnpm run build -- --watch" } } ``` Furthermore, we were trying to fix issues where our integration test setup could not install `tailwindcss@3` because of how we did pnpm overrides. This PR fixes this by: - Ensuring every client that calls into `tailwindcss` core marks it as a version-pinned dependency. You are still required to install `tailwindcss` in your project along side a client (e.g. `@tailwindcss/vite`) but we now only use your installed version for importing the respective `.css` files. For the core logic, we are now requiring each package to use `tailwindcss` at the same version. This should help resolve issues like https://github.com/tailwindlabs/tailwindcss/discussions/14652 - We tried to eliminate the dependency on `tailwindcss` from the `@tailwindcss/upgrade` package. Unfortunately this is not possible to do right now because we need to load the CSS files from v4 to create the right environment. In a future version we could bundle the required CSS files with `@tailwidncss/upgrade` but it doesn't seem necessary for now. - We then changed our integration test overrides to only override the `tailwindcss` package that are dependencies of the known list of packages that we have `tailwindcss` dependencies on: `@tailwindcss/node` and `@tailwindcss/upgrade`. This ensures that we can install v3 of `tailwindcss` in the integration tests and it will work. Something we want to do for some upgrade tests. # Test plan Integration work again. Furthermore we added a quick setup with the CLI using the local tarballs and ensured it works: ```bash pnpm init pnpm install ../../tailwindcss/dist/tailwindcss-cli.tgz pnpm install ../../tailwindcss/dist/tailwindcss.tgz echo '@import "tailwindcss";' > index.css echo '<div class="underline"></div>' > index.html pnpm tailwindcss -i index.css -o out.css cat out.css ```
2024-11-15 17:18:48 +01:00
} else {
json.pnpm.overrides[pkg] = resolveVersion(pkg)
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
}
Add integration test setup and tests for the Vite integration (#14089) This PR adds a new root `/integrations` folder that will be the home of integration tests. The idea of these tests is to use Tailwind in various setups just like our users would (by only using the publishable npm builds). To avoid issues with concurrent tests making changes to the file system, to make it very easy to test through a range of versions, and to avoid changing configuration objects over and over in test runs, we decided to inline the scaffolding completely into the test file and have no examples checked into the repo. Here's an example of how this can look like for a simple Vite test: ```ts test('works with production builds', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css"> </head> <body> <div class="underline m-2">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss/theme' reference; @import 'tailwindcss/utilities'; `, }, }, async ({ fs, exec }) => { await exec('pnpm vite build') expect.assertions(2) for (let [path, content] of await fs.glob('dist/**/*.css')) { expect(path).toMatch(/\.css$/) expect(stripTailwindComment(content)).toMatchInlineSnapshot( ` ".m-2 { margin: var(--spacing-2, .5rem); } .underline { text-decoration-line: underline; }" `, ) } }, ) ``` By defining all dependencies this way, we never have to worry about which fixtures are checked in and can more easily describe changes to the setup. For ergonomics, we've also added the [`embed` prettier plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will mean that files inlined in the `fs` setup are properly indented. No extra work needed! If you're using VS Code, I can also recommend the [Language Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals) extension so that syntax highlighting also _just works_. A neat feature of inlining the scaffolding like this is to make it very simple to test through a variety of versions. For example, here's how we can set up a test against Vite 5 and Vite 4: ```js ;['^4.5.3', '^5.3.5'].forEach(viteVersion => { test(`works with production builds for Vite ${viteVersion}`, { fs: { 'package.json': json` { "type": "module", "devDependencies": { "vite": "${viteVersion}" } } `, async () => { // Do something }, ) }) ``` ## Philosophy Before we dive into the specifics, I want to clearly state the design considerations we have chosen for this new test suite: - All file mutations should be done in temp folders, nothing should ever mess with your working directory - Windows as a first-class citizen - Have a clean and simple API that describes the test setup only using public APIs - Focus on reliability (make sure cleanup scripts work and are tolerant to various error scenarios) - If a user reports an issue with a specific configuration, we want to be able to reproduce them with integration tests, no matter how obscure the setup (this means the test need to be in control of most of the variables) - Tests should be reasonably fast (obviously this depends on the integration. If we use a slow build tool, we can't magically speed it up, but our overhead should be minimal). ## How it works The current implementation provides a custom `test` helper function that, when used, sets up the environment according to the configuration. It'll create a new temporary directory and create all files, ensuring things like proper `\r\n` line endings on Windows. We do have to patch the `package.json` specifically, since we can not use public versions of the tailwindcss packages as we want to be able to test against a development build. To make this happen, every `pnpm build` run now creates tarballs of the npm modules (that contain only the files that would also in the published build). We then patch the `package.json` to rewrite `workspace:^` versions to link to those tarballs. We found this to work reliably on Windows and macOS as well as being fast enough to not cause any issues. Furthermore we also decided to use `pnpm` as the version manager for integration tests because of it's global module cache (so installing `vite` is fast as soon as you installed it once). The test function will receive a few utilities that it can use to more easily interact with the temp dir. One example is a `fs.glob` function that you can use to easily find files in eventual `dist/` directories or helpers around `spawn` and `exec` that make sure that processes are cleaned up correctly. Because we use tarballs from our build dependencies, working on changes requires a workflow where you run `pnpm build` before running `pnpm test:integrations`. However it also means we can run clients like our CLI client with no additional overhead—just install the dependency like any user would and set up your test cases this way. ## Test plan This PR also includes two Vite specific integration tests: One testing a static build (`pnpm vite build`) and one a dev mode build (`pnpm vite dev`) that also makes changes to the file system and asserts that the resources properly update. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 11:50:49 +02:00
return JSON.stringify(json, null, 2)
}
function resolveVersion(dependency: string) {
let tarball = path.join(REPO_ROOT, 'dist', pkgToFilename(dependency))
return `file:${tarball}`
}
export function stripTailwindComment(content: string) {
return content.replace(/\/\*! tailwindcss .*? \*\//g, '').trim()
}
export let svg = dedent
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
export let css = dedent
export let html = dedent
export let ts = dedent
export let js = dedent
export let jsx = dedent
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
export let json = dedent
export let yaml = dedent
export let txt = dedent
export function binary(str: string | TemplateStringsArray, ...values: unknown[]): Uint8Array {
let base64 = typeof str === 'string' ? str : String.raw(str, ...values)
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0))
}
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
export function candidate(strings: TemplateStringsArray, ...values: any[]) {
let output: string[] = []
for (let i = 0; i < strings.length; i++) {
output.push(strings[i])
if (i < values.length) {
output.push(values[i])
}
}
return `.${escape(output.join('').trim())}`
}
export async function retryAssertion<T>(
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
fn: () => Promise<T>,
{ timeout = ASSERTION_TIMEOUT, delay = 5 }: { timeout?: number; delay?: number } = {},
) {
let end = Date.now() + timeout
let error: any
while (Date.now() < end) {
try {
return await fn()
} catch (err) {
error = err
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
throw error
}
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
export async function fetchStyles(base: string, path = '/'): Promise<string> {
while (base.endsWith('/')) {
base = base.slice(0, -1)
}
let index = await fetch(`${base}${path}`)
let html = await index.text()
let linkRegex = /<link rel="stylesheet" href="([a-zA-Z0-9\/_\.\?=%-]+)"/gi
let styleRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi
let stylesheets: string[] = []
let paths: string[] = []
for (let match of html.matchAll(linkRegex)) {
let path: string = match[1]
if (path.startsWith('./')) {
path = path.slice(1)
}
paths.push(path)
}
stylesheets.push(
...(await Promise.all(
paths.map(async (path) => {
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
let css = await fetch(`${base}${path}`, {
headers: {
Accept: 'text/css',
},
})
return await css.text()
}),
)),
)
for (let match of html.matchAll(styleRegex)) {
stylesheets.push(match[1])
}
return stylesheets.reduce((acc, css) => {
return acc + '\n' + css
}, '')
}
async function gracefullyRemove(dir: string) {
// Skip removing the directory in CI because it can stall on Windows
if (!process.env.CI) {
await fs.rm(dir, { recursive: true, force: true }).catch((error) => {
console.log(`Failed to remove ${dir}`, error)
})
}
}