14 Commits

Author SHA1 Message Date
Philipp Spiess
78b75e4d96
Only run Ubuntu tests on feature branches (#14560)
This PR changes it so that only the Ubuntu runner starts when doing a
pull request. On a successfull `next` merge, all runners shoould start.

Furthermore this increases the retry count for integration test to 3.
This is mainly so that rare Windows flakes we still see won't become
noise when we enabled the Discord notification.
2024-10-01 13:45:52 +02:00
LongYinan
bca04dfe00
ci: speedup windows integrations tests (#14485)
- Avoid cross-device copying in Windows CI by setting the tests dir to
the same drive as the workspace.
- Disable LTO and use a faster linker for the Rust build 

Buid: ~3min -> ~2min
Integration Tests: ~8min -> ~3min20s
2024-09-20 15:59:18 -04:00
Philipp Spiess
63390c97b2
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
Philipp Spiess
4297655a7a
Add integration test for Vite CSS module support (#14349)
Wanted to make sure stuff works with CSS modules for both the postcss
and the lightningcss pipeline.
2024-09-05 14:24:50 +02:00
Philipp Spiess
2dd52f5a0f
Vite: Add support for <style> tags in Astro files (#14340)
This works similar to the Vue setup. The styles that Astro will receive
might still contain Tailwind CSS APIs but since it's not picky, we can
pass that through to the regular Vite `transform` handlers for now.

This, however, will have issues like
https://github.com/tailwindlabs/tailwindcss/issues/14205. We have to fix
this together with Vue and other similar extensions later. For now, it
will break when syntax is used that lightningcss rewrites (like `@apply
text-3xl/tight;`)

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-04 19:14:05 +02:00
Philipp Spiess
835922812b
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
Jordan Pittman
30bbe51a38
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
Philipp Spiess
84ebe19da2
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
Philipp Spiess
e10b786437
Add Next.js integration test (#14163)
Using the [new integration test
setup](https://github.com/tailwindlabs/tailwindcss/pull/14089), this PR
adds a test for a V4 Next.js setup using the Postcss plugin. It's
testing both a full build and the dev mode (non-turbo for now).

Because of webpack, tests are quite slow which is worrisome since we
probably need to add many more integrations in the future. One idea I
have is that we separate tests in two buckets: _essential_ tests that
run often and are fast and advanced suites that we only run on CI via
custom, non-blocking, jobs.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:45:52 +02:00
Philipp Spiess
b01ff53f2a
Vite: Support Tailwind in Vue <style> blocks (#14158)
This PR adds support to transforming `<style>` blocks emitted by Vue
components with tailwindcss when the `@tailwindcss/vite` is used.

Example:

```vue
<style>
  @import 'tailwindcss/utilities';
  @import 'tailwindcss/theme' theme(reference);
  .foo {
    @apply text-red-500;
  }
</style>
<template>
 <div class="underline foo">Hello Vue!</div>
</template>
```

Additionally, this PR also adds an integration test.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-09 12:41:58 +02:00
Philipp Spiess
921b4b673b
Use import to load plugins (#14132)
Alternative to #14110

This PR changes the way how we load plugins to be compatible with ES6
async `import`s. This allows us to load plugins even inside the browser
but it comes at a downside: We now have to change the `compile` API to
return a `Promise`...

So most of this PR is rewriting all of the call sites of `compile` to
expect a promise instead of the object.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-08 11:49:06 -04:00
Philipp Spiess
af2e69959d
Add test.debug to integration tests (#14133)
While working on #14078, there were a couple of debugging techniques
that we were using quite frequently:

- Being able to `cd` into the test setup
- Seeing the stdio and stdout data in real-time (this currently requires
us to mark a test as failing)
- Checking the exact commands that are being run

Since we naturally worked around this quite often, I decided to make
this a first-level API with the introduction of a new `test.debug` flag.
When set, it will:

- Create the test setup in the project dir within a new `.debug` folder
and won't delete it after the run. Having it in an explicit folder
allows us to easily delete it manually when we need to.
- Logs all run commands to the console (`>` for a sync call, `>&` for a
spawned process)
- Logs stdio and stderr to the console in real time.
- Run the test as `.only`

<img width="2267" alt="Screenshot 2024-08-06 at 13 19 49"
src="https://github.com/user-attachments/assets/1b204ac2-feee-489e-9cd8-edf73c0f2abd">

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-08 12:02:19 +02:00
Robin Malfait
541d84a3bb
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
Philipp Spiess
27912f9bb5
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