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
|
|
|
import dedent from 'dedent'
|
2024-08-07 16:38:44 +02:00
|
|
|
import path from 'node:path'
|
2024-11-19 18:39:49 +01:00
|
|
|
import { candidate, css, html, js, json, test, ts, yaml } from '../utils'
|
2024-08-07 16:38:44 +02:00
|
|
|
|
|
|
|
|
test(
|
2024-08-08 17:49:06 +02:00
|
|
|
'production build (string)',
|
2024-08-07 16:38:44 +02:00
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="underline 2xl:font-bold hocus:underline inverted:flex"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/plugin.js': js`
|
|
|
|
|
module.exports = function ({ addVariant }) {
|
|
|
|
|
addVariant('inverted', '@media (inverted-colors: inverted)')
|
|
|
|
|
addVariant('hocus', ['&:focus', '&:hover'])
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-a/tailwind.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
content: ['../project-b/src/**/*.js'],
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
'project-a/src/index.css': css`
|
Resolve `@import` in core (#14446)
This PR brings `@import` resolution into Tailwind CSS core. This means
that our clients (PostCSS, Vite, and CLI) no longer need to depend on
`postcss` and `postcss-import` to resolve `@import`. Furthermore this
simplifies the handling of relative paths for `@source`, `@plugin`, or
`@config` in transitive CSS files (where the relative root should always
be relative to the CSS file that contains the directive). This PR also
fixes a plugin resolution bug where non-relative imports (e.g. directly
importing node modules like `@plugin '@tailwindcss/typography';`) would
not work in CSS files that are based in a different npm package.
### Resolving `@import`
The core of the `@import` resolution is inside
`packages/tailwindcss/src/at-import.ts`. There, to keep things
performant, we do a two-step process to resolve imports. Imagine the
following input CSS file:
```css
@import "tailwindcss/theme.css";
@import "tailwindcss/utilities.css";
```
Since our AST walks are synchronous, we will do a first traversal where
we start a loading request for each `@import` directive. Once all loads
are started, we will await the promise and do a second walk where we
actually replace the AST nodes with their resolved stylesheets. All of
this is recursive, so that `@import`-ed files can again `@import` other
files.
The core `@import` resolver also includes extensive test cases for
[various combinations of media query and supports conditionals as well
als layered
imports](https://developer.mozilla.org/en-US/docs/Web/CSS/@import).
When the same file is imported multiple times, the AST nodes are
duplicated but duplicate I/O is avoided on a per-file basis, so this
will only load one file, but include the `@theme` rules twice:
```css
@import "tailwindcss/theme.css";
@import "tailwindcss/theme.css";
```
### Adding a new `context` node to the AST
One limitation we had when working with the `postcss-import` plugin was
the need to do an additional traversal to rewrite relative `@source`,
`@plugin`, and `@config` directives. This was needed because we want
these paths to be relative to the CSS file that defines the directive
but when flattening a CSS file, this information is no longer part of
the stringifed CSS representation. We worked around this by rewriting
the content of these directives to be relative to the input CSS file,
which resulted in added complexity and caused a lot of issues with
Windows paths in the beginning.
Now that we are doing the `@import` resolution in core, we can use a
different data structure to persist this information. This PR adds a new
`context` node so that we can store arbitrary context like this inside
the Ast directly. This allows us to share information with the sub tree
_while doing the Ast walk_.
Here's an example of how the new `context` node can be used to share
information with subtrees:
```ts
const ast = [
rule('.foo', [decl('color', 'red')]),
context({ value: 'a' }, [
rule('.bar', [
decl('color', 'blue'),
context({ value: 'b' }, [
rule('.baz', [decl('color', 'green')]),
]),
]),
]),
]
walk(ast, (node, { context }) => {
if (node.kind !== 'declaration') return
switch (node.value) {
case 'red': assert(context.value === undefined)
case 'blue': assert(context.value === 'a')
case 'green': assert(context.value === 'b')
}
})
```
In core, we use this new Ast node specifically to persist the `base`
path of the current CSS file. We put the input CSS file `base` at the
root of the Ast and then overwrite the `base` on every `@import`
substitution.
### Removing the dependency on `postcss-import`
Now that we support `@import` resolution in core, our clients no longer
need a dependency on `postcss-import`. Furthermore, most dependencies
also don't need to know about `postcss` at all anymore (except the
PostCSS client, of course!).
This also means that our workaround for rewriting `@source`, the
`postcss-fix-relative-paths` plugin, can now go away as a shared
dependency between all of our clients. Note that we still have it for
the PostCSS plugin only, where it's possible that users already have
`postcss-import` running _before_ the `@tailwindcss/postcss` plugin.
Here's an example of the changes to the dependencies for our Vite client
✨ :
<img width="854" alt="Screenshot 2024-09-19 at 16 59 45"
src="https://github.com/user-attachments/assets/ae1f9d5f-d93a-4de9-9244-61af3aff1237">
### Performance
Since our Vite and CLI clients now no longer need to use `postcss` at
all, we have also measured a significant improvement to the initial
build times. For a small test setup that contains only a hand full of
files (nothing super-complex), we measured an improvement in the
**3.5x** range:
<img width="1334" alt="Screenshot 2024-09-19 at 14 52 49"
src="https://github.com/user-attachments/assets/06071fb0-7f2a-4de6-8ec8-f202d2cc78e5">
The code for this is in the commit history if you want to reproduce the
results. The test was based on the Vite client.
### Caveats
One thing to note is that we previously relied on finding specific
symbols in the input CSS to _bail out of Tailwind processing
completely_. E.g. if a file does not contain a `@tailwind` or `@apply`
directive, it can never be a Tailwind file.
Since we no longer have a string representation of the flattened CSS
file, we can no longer do this check. However, the current
implementation was already inconsistent with differences on the allowed
symbol list between our clients. Ideally, Tailwind CSS should figure out
wether a CSS file is a Tailwind CSS file. This, however, is left as an
improvement for a future API since it goes hand-in-hand with our planned
API changes for the core `tailwindcss` package.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-23 17:05:55 +02:00
|
|
|
@import 'tailwindcss/utilities';
|
|
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
|
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.js': js`
|
|
|
|
|
const className = "content-['a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
|
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div class="flex" />
|
|
|
|
|
`,
|
|
|
|
|
'project-b/src/index.js': js`
|
|
|
|
|
const className = "content-['b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, exec }) => {
|
|
|
|
|
await exec('pnpm postcss src/index.css --output dist/out.css', {
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`underline`,
|
|
|
|
|
candidate`flex`,
|
|
|
|
|
candidate`content-['a/src/index.js']`,
|
|
|
|
|
candidate`content-['b/src/index.js']`,
|
|
|
|
|
candidate`inverted:flex`,
|
|
|
|
|
candidate`hocus:underline`,
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'production build with `postcss-import` (string)',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"postcss-import": "^16",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'postcss-import': {},
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="underline 2xl:font-bold hocus:underline inverted:flex"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/plugin.js': js`
|
|
|
|
|
module.exports = function ({ addVariant }) {
|
|
|
|
|
addVariant('inverted', '@media (inverted-colors: inverted)')
|
|
|
|
|
addVariant('hocus', ['&:focus', '&:hover'])
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/tailwind.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
content: ['../project-b/src/**/*.js'],
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.css': css`
|
2024-08-07 16:38:44 +02:00
|
|
|
@import 'tailwindcss/utilities';
|
2024-09-03 16:54:08 +02:00
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
2024-08-07 16:38:44 +02:00
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.js': js`
|
|
|
|
|
const className = "content-['a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div class="flex" />
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
'project-b/src/index.js': js`
|
|
|
|
|
const className = "content-['b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
2024-08-08 17:49:06 +02:00
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, exec }) => {
|
|
|
|
|
await exec('pnpm postcss src/index.css --output dist/out.css', {
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`underline`,
|
2024-09-03 16:54:08 +02:00
|
|
|
candidate`flex`,
|
2024-08-08 17:49:06 +02:00
|
|
|
candidate`content-['a/src/index.js']`,
|
|
|
|
|
candidate`content-['b/src/index.js']`,
|
|
|
|
|
candidate`inverted:flex`,
|
|
|
|
|
candidate`hocus:underline`,
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'production build (ESM)',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.mjs': js`
|
|
|
|
|
import tailwindcss from '@tailwindcss/postcss'
|
|
|
|
|
export default {
|
|
|
|
|
plugins: [tailwindcss()],
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="underline 2xl:font-bold hocus:underline inverted:flex"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/plugin.js': js`
|
|
|
|
|
module.exports = function ({ addVariant }) {
|
|
|
|
|
addVariant('inverted', '@media (inverted-colors: inverted)')
|
|
|
|
|
addVariant('hocus', ['&:focus', '&:hover'])
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-a/tailwind.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
content: ['../project-b/src/**/*.js'],
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-08-08 17:49:06 +02:00
|
|
|
'project-a/src/index.css': css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
2024-09-03 16:54:08 +02:00
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
2024-08-08 17:49:06 +02:00
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.js': js`
|
|
|
|
|
const className = "content-['a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div class="flex" />
|
|
|
|
|
`,
|
2024-08-08 17:49:06 +02:00
|
|
|
'project-b/src/index.js': js`
|
|
|
|
|
const className = "content-['b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, exec }) => {
|
|
|
|
|
await exec('pnpm postcss src/index.css --output dist/out.css', {
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`underline`,
|
2024-09-03 16:54:08 +02:00
|
|
|
candidate`flex`,
|
2024-08-08 17:49:06 +02:00
|
|
|
candidate`content-['a/src/index.js']`,
|
|
|
|
|
candidate`content-['b/src/index.js']`,
|
|
|
|
|
candidate`inverted:flex`,
|
|
|
|
|
candidate`hocus:underline`,
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'production build (CJS)',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.cjs': js`
|
|
|
|
|
let tailwindcss = require('@tailwindcss/postcss')
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: [tailwindcss()],
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="underline 2xl:font-bold hocus:underline inverted:flex"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/plugin.js': js`
|
|
|
|
|
module.exports = function ({ addVariant }) {
|
|
|
|
|
addVariant('inverted', '@media (inverted-colors: inverted)')
|
|
|
|
|
addVariant('hocus', ['&:focus', '&:hover'])
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-a/tailwind.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
content: ['../project-b/src/**/*.js'],
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-08-08 17:49:06 +02:00
|
|
|
'project-a/src/index.css': css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
2024-09-03 16:54:08 +02:00
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
2024-08-08 17:49:06 +02:00
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.js': js`
|
|
|
|
|
const className = "content-['a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div class="flex" />
|
|
|
|
|
`,
|
2024-08-08 17:49:06 +02:00
|
|
|
'project-b/src/index.js': js`
|
|
|
|
|
const className = "content-['b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
2024-08-07 16:38:44 +02:00
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, exec }) => {
|
|
|
|
|
await exec('pnpm postcss src/index.css --output dist/out.css', {
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`underline`,
|
2024-09-03 16:54:08 +02:00
|
|
|
candidate`flex`,
|
2024-08-07 16:38:44 +02:00
|
|
|
candidate`content-['a/src/index.js']`,
|
|
|
|
|
candidate`content-['b/src/index.js']`,
|
|
|
|
|
candidate`inverted:flex`,
|
|
|
|
|
candidate`hocus:underline`,
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
2024-11-19 18:39:49 +01:00
|
|
|
test(
|
|
|
|
|
'module resolution using CJS, ESM, CTS, and MTS',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-cjs
|
|
|
|
|
- project-esm
|
|
|
|
|
- plugin-cjs
|
|
|
|
|
- plugin-esm
|
|
|
|
|
- plugin-cts
|
|
|
|
|
- plugin-mts
|
|
|
|
|
`,
|
|
|
|
|
'project-cjs/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"type": "commonjs",
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^",
|
|
|
|
|
"plugin-cjs": "workspace:*",
|
|
|
|
|
"plugin-cts": "workspace:*",
|
|
|
|
|
"plugin-esm": "workspace:*",
|
|
|
|
|
"plugin-mts": "workspace:*",
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-cjs/postcss.config.cjs': js`
|
|
|
|
|
let tailwindcss = require('@tailwindcss/postcss')
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: [tailwindcss()],
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-cjs/index.html': html`
|
|
|
|
|
<div class="cjs esm cts mts"></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-cjs/src/index.css': css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
|
|
|
|
@plugin 'plugin-cjs';
|
|
|
|
|
@plugin 'plugin-esm';
|
|
|
|
|
@plugin 'plugin-cts';
|
|
|
|
|
@plugin 'plugin-mts';
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'project-esm/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"type": "module",
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^",
|
|
|
|
|
"plugin-cjs": "workspace:*",
|
|
|
|
|
"plugin-cts": "workspace:*",
|
|
|
|
|
"plugin-esm": "workspace:*",
|
|
|
|
|
"plugin-mts": "workspace:*",
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-esm/postcss.config.mjs': js`
|
|
|
|
|
import tailwindcss from '@tailwindcss/postcss'
|
|
|
|
|
export default {
|
|
|
|
|
plugins: [tailwindcss()],
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-esm/index.html': html`
|
|
|
|
|
<div class="cjs esm cts mts"></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-esm/src/index.css': css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
|
|
|
|
@plugin 'plugin-cjs';
|
|
|
|
|
@plugin 'plugin-esm';
|
|
|
|
|
@plugin 'plugin-cts';
|
|
|
|
|
@plugin 'plugin-mts';
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'plugin-cjs/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"name": "plugin-cjs",
|
|
|
|
|
"type": "commonjs",
|
|
|
|
|
"exports": {
|
|
|
|
|
".": {
|
|
|
|
|
"require": "./index.cjs"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'plugin-cjs/index.cjs': js`
|
|
|
|
|
module.exports = function ({ addUtilities }) {
|
|
|
|
|
addUtilities({ '.cjs': { content: '"cjs"' } })
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'plugin-esm/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"name": "plugin-esm",
|
|
|
|
|
"type": "module",
|
|
|
|
|
"exports": {
|
|
|
|
|
".": {
|
|
|
|
|
"import": "./index.mjs"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'plugin-esm/index.mjs': js`
|
|
|
|
|
export default function ({ addUtilities }) {
|
|
|
|
|
addUtilities({ '.esm': { content: '"esm"' } })
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'plugin-cts/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"name": "plugin-cts",
|
|
|
|
|
"type": "commonjs",
|
|
|
|
|
"exports": {
|
|
|
|
|
".": {
|
|
|
|
|
"require": "./index.cts"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'plugin-cts/index.cts': ts`
|
|
|
|
|
export default function ({ addUtilities }) {
|
|
|
|
|
addUtilities({ '.cts': { content: '"cts"' as const } })
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'plugin-mts/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"name": "plugin-mts",
|
|
|
|
|
"type": "module",
|
|
|
|
|
"exports": {
|
|
|
|
|
".": {
|
|
|
|
|
"import": "./index.mts"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'plugin-mts/index.mts': ts`
|
|
|
|
|
export default function ({ addUtilities }) {
|
|
|
|
|
addUtilities({ '.mts': { content: '"mts"' as const } })
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, exec }) => {
|
|
|
|
|
await exec(`pnpm postcss src/index.css --output dist/out.css`, {
|
|
|
|
|
cwd: path.join(root, 'project-cjs'),
|
|
|
|
|
})
|
|
|
|
|
await exec(`pnpm postcss src/index.css --output dist/out.css`, {
|
|
|
|
|
cwd: path.join(root, 'project-esm'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('./project-cjs/dist/out.css', [
|
|
|
|
|
candidate`cjs`,
|
|
|
|
|
candidate`esm`,
|
|
|
|
|
candidate`cts`,
|
|
|
|
|
candidate`mts`,
|
|
|
|
|
])
|
|
|
|
|
await fs.expectFileToContain('./project-esm/dist/out.css', [
|
|
|
|
|
candidate`cjs`,
|
|
|
|
|
candidate`esm`,
|
|
|
|
|
candidate`cts`,
|
|
|
|
|
candidate`mts`,
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
2024-08-07 16:38:44 +02:00
|
|
|
test(
|
|
|
|
|
'watch mode',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/index.html': html`
|
|
|
|
|
<div
|
2024-10-01 13:52:12 +02:00
|
|
|
class="underline 2xl:font-bold hocus:underline inverted:flex text-primary"
|
2024-08-07 16:38:44 +02:00
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/plugin.js': js`
|
|
|
|
|
module.exports = function ({ addVariant }) {
|
|
|
|
|
addVariant('inverted', '@media (inverted-colors: inverted)')
|
|
|
|
|
addVariant('hocus', ['&:focus', '&:hover'])
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-a/tailwind.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
content: ['../project-b/src/**/*.js'],
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
'project-a/src/index.css': css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
2024-10-01 13:52:12 +02:00
|
|
|
@import './custom-theme.css';
|
2024-09-03 16:54:08 +02:00
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
2024-08-07 16:38:44 +02:00
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
`,
|
2024-10-01 13:52:12 +02:00
|
|
|
'project-a/src/custom-theme.css': css`
|
|
|
|
|
/* Will be overwritten later */
|
|
|
|
|
@theme {
|
|
|
|
|
--color-primary: black;
|
|
|
|
|
}
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
'project-a/src/index.js': js`
|
|
|
|
|
const className = "content-['a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-09-03 16:54:08 +02:00
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div class="flex" />
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
'project-b/src/index.js': js`
|
|
|
|
|
const className = "content-['b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-10-03 16:21:54 +02:00
|
|
|
'project-c/src/index.js': js`
|
|
|
|
|
const className = "content-['c/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async ({ root, fs, spawn }) => {
|
|
|
|
|
let process = await spawn(
|
|
|
|
|
'pnpm postcss src/index.css --output dist/out.css --watch --verbose',
|
|
|
|
|
{ cwd: path.join(root, 'project-a') },
|
|
|
|
|
)
|
|
|
|
|
await process.onStderr((message) => message.includes('Waiting for file changes...'))
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`underline`,
|
2024-09-03 16:54:08 +02:00
|
|
|
candidate`flex`,
|
2024-08-07 16:38:44 +02:00
|
|
|
candidate`content-['a/src/index.js']`,
|
|
|
|
|
candidate`content-['b/src/index.js']`,
|
|
|
|
|
candidate`inverted:flex`,
|
|
|
|
|
candidate`hocus:underline`,
|
2024-10-01 13:52:12 +02:00
|
|
|
css`
|
|
|
|
|
.text-primary {
|
2024-11-05 15:44:21 -05:00
|
|
|
color: var(--color-primary);
|
2024-10-01 13:52:12 +02:00
|
|
|
}
|
|
|
|
|
`,
|
2024-08-07 16:38:44 +02:00
|
|
|
])
|
|
|
|
|
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/src/index.js',
|
|
|
|
|
js`
|
|
|
|
|
const className = "[.changed_&]:content-['project-a/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-a/src/index.js']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-b/src/index.js',
|
|
|
|
|
js`
|
|
|
|
|
const className = "[.changed_&]:content-['project-b/src/index.js']"
|
|
|
|
|
module.exports = { className }
|
|
|
|
|
`,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-b/src/index.js']`,
|
|
|
|
|
])
|
2024-10-01 13:52:12 +02:00
|
|
|
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/src/custom-theme.css',
|
|
|
|
|
css`
|
|
|
|
|
/* Overriding the primary color */
|
|
|
|
|
@theme {
|
|
|
|
|
--color-primary: red;
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [
|
|
|
|
|
css`
|
|
|
|
|
.text-primary {
|
2024-11-05 15:44:21 -05:00
|
|
|
color: var(--color-primary);
|
2024-10-01 13:52:12 +02:00
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
])
|
2024-10-03 16:21:54 +02:00
|
|
|
|
|
|
|
|
// Adding a new @source directive will scan for new candidates
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/src/index.css',
|
|
|
|
|
css`
|
|
|
|
|
@import 'tailwindcss/utilities';
|
|
|
|
|
@import './custom-theme.css';
|
|
|
|
|
@config '../tailwind.config.js';
|
|
|
|
|
@source '../../project-b/src/**/*.html';
|
|
|
|
|
@plugin '../plugin.js';
|
|
|
|
|
@source '../../project-c/src/**/*.js';
|
|
|
|
|
`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('project-a/dist/out.css', [candidate`content-['c/src/index.js']`])
|
2024-08-07 16:38:44 +02:00
|
|
|
},
|
|
|
|
|
)
|
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
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'auto source detection kitchen sink',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'index.css': css`
|
2025-02-25 11:36:43 +01:00
|
|
|
@reference 'tailwindcss/theme';
|
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
|
|
|
|
|
|
|
|
/* (1) */
|
|
|
|
|
/* - Only './src' should be auto-scanned, not the current working directory */
|
|
|
|
|
/* - .gitignore'd paths should be ignored (node_modules) */
|
|
|
|
|
/* - Binary extensions should be ignored (jpg, zip) */
|
|
|
|
|
@import 'tailwindcss/utilities' source('./src');
|
|
|
|
|
|
|
|
|
|
/* (2) */
|
|
|
|
|
/* - All HTML and JSX files in 'ignored/components' should be scanned */
|
|
|
|
|
/* - All other extensions should be ignored */
|
|
|
|
|
@source "./ignored/components/*.{html,jsx}";
|
|
|
|
|
|
|
|
|
|
/* (3) */
|
|
|
|
|
/* - './components' should be auto-scanned in addition to './src' */
|
|
|
|
|
/* - './components/ignored.html' should still be ignored */
|
|
|
|
|
/* - Binary extensions in './components' should be ignored */
|
|
|
|
|
@source "./components";
|
|
|
|
|
|
|
|
|
|
/* (4) */
|
|
|
|
|
/* - './pages' should be auto-scanned */
|
|
|
|
|
/* - Only '.html' files should be included */
|
|
|
|
|
/* - './page/ignored.html' should be ignored */
|
|
|
|
|
@source "./pages/**/*.html";
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'.gitignore': dedent`
|
|
|
|
|
/src/ignored
|
|
|
|
|
/ignored
|
|
|
|
|
/components/ignored.html
|
|
|
|
|
/pages/ignored.html
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// (1)
|
|
|
|
|
'index.html': 'content-["index.html"] content-["BAD"]', // "Root" source is in `./src`
|
|
|
|
|
'src/index.html': 'content-["src/index.html"]',
|
|
|
|
|
'src/nested/index.html': 'content-["src/nested/index.html"]',
|
|
|
|
|
'src/index.jpg': 'content-["src/index.jpg"] content-["BAD"]',
|
|
|
|
|
'src/nested/index.tar': 'content-["src/nested/index.tar"] content-["BAD"]',
|
|
|
|
|
'src/ignored/index.html': 'content-["src/ignored/index.html"] content-["BAD"]',
|
|
|
|
|
|
|
|
|
|
// (2)
|
|
|
|
|
'ignored/components/my-component.html': 'content-["ignored/components/my-component.html"]',
|
|
|
|
|
'ignored/components/my-component.jsx': 'content-["ignored/components/my-component.jsx"]',
|
|
|
|
|
|
|
|
|
|
// Ignored and not explicitly listed by (2)
|
|
|
|
|
'ignored/components/my-component.tsx':
|
|
|
|
|
'content-["ignored/components/my-component.tsx"] content-["BAD"]',
|
|
|
|
|
'ignored/components/nested/my-component.html':
|
|
|
|
|
'content-["ignored/components/nested/my-component.html"] content-["BAD"]',
|
|
|
|
|
|
|
|
|
|
// (3)
|
|
|
|
|
'components/my-component.tsx': 'content-["components/my-component.tsx"]',
|
|
|
|
|
'components/nested/my-component.tsx': 'content-["components/nested/my-component.tsx"]',
|
|
|
|
|
'components/ignored.html': 'content-["components/ignored.html"] content-["BAD"]',
|
|
|
|
|
|
|
|
|
|
// (4)
|
|
|
|
|
'pages/foo.html': 'content-["pages/foo.html"]',
|
|
|
|
|
'pages/nested/foo.html': 'content-["pages/nested/foo.html"]',
|
|
|
|
|
'pages/ignored.html': 'content-["pages/ignored.html"] content-["BAD"]',
|
|
|
|
|
'pages/foo.jsx': 'content-["pages/foo.jsx"] content-["BAD"]',
|
|
|
|
|
'pages/nested/foo.jsx': 'content-["pages/nested/foo.jsx"] content-["BAD"]',
|
|
|
|
|
},
|
|
|
|
|
},
|
Improve integration tests (stability + performance) (#15125)
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
2024-12-12 13:48:56 +01:00
|
|
|
async ({ fs, exec, expect }) => {
|
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
|
|
|
await exec('pnpm postcss index.css --output dist/out.css')
|
|
|
|
|
|
|
|
|
|
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
|
|
|
|
|
"
|
|
|
|
|
--- ./dist/out.css ---
|
|
|
|
|
.content-\\[\\"components\\/my-component\\.tsx\\"\\] {
|
|
|
|
|
--tw-content: "components/my-component.tsx";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"components\\/nested\\/my-component\\.tsx\\"\\] {
|
|
|
|
|
--tw-content: "components/nested/my-component.tsx";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"ignored\\/components\\/my-component\\.html\\"\\] {
|
|
|
|
|
--tw-content: "ignored/components/my-component.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"ignored\\/components\\/my-component\\.jsx\\"\\] {
|
|
|
|
|
--tw-content: "ignored/components/my-component.jsx";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"pages\\/foo\\.html\\"\\] {
|
|
|
|
|
--tw-content: "pages/foo.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"pages\\/nested\\/foo\\.html\\"\\] {
|
|
|
|
|
--tw-content: "pages/nested/foo.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"src\\/index\\.html\\"\\] {
|
|
|
|
|
--tw-content: "src/index.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"src\\/nested\\/index\\.html\\"\\] {
|
|
|
|
|
--tw-content: "src/nested/index.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
@property --tw-content {
|
|
|
|
|
syntax: "*";
|
|
|
|
|
inherits: false;
|
|
|
|
|
initial-value: "";
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
`)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'auto source detection in depth, source(…) and `@source` can be configured to use auto source detection (build + watch mode)',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`{}`,
|
|
|
|
|
'pnpm-workspace.yaml': yaml`
|
|
|
|
|
#
|
|
|
|
|
packages:
|
|
|
|
|
- project-a
|
|
|
|
|
`,
|
|
|
|
|
'project-a/package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'project-a/src/index.css': css`
|
2025-02-25 11:36:43 +01:00
|
|
|
@reference 'tailwindcss/theme';
|
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
|
|
|
|
|
|
|
|
/* Run auto-content detection in ../../project-b */
|
|
|
|
|
@import 'tailwindcss/utilities' source('../../project-b');
|
|
|
|
|
|
|
|
|
|
/* Explicitly using node_modules in the @source allows git ignored folders */
|
|
|
|
|
@source '../node_modules/{my-lib-1,my-lib-2}/src/**/*.html';
|
|
|
|
|
|
|
|
|
|
/* We typically ignore these extensions, but now include them explicitly */
|
|
|
|
|
@source './logo.{jpg,png}';
|
|
|
|
|
|
|
|
|
|
/* Project C should apply auto source detection */
|
|
|
|
|
@source '../../project-c';
|
|
|
|
|
|
|
|
|
|
/* Project D should apply auto source detection rules, such as ignoring node_modules */
|
|
|
|
|
@source '../../project-d/**/*.{html,js}';
|
|
|
|
|
@source '../../project-d/**/*.bin';
|
|
|
|
|
|
|
|
|
|
/* Same as above, but my-lib-2 _should_ be includes */
|
|
|
|
|
@source '../../project-d/node_modules/my-lib-2/src/*.{html,js}';
|
|
|
|
|
|
|
|
|
|
/* bar.html is git ignored, but explicitly listed here to scan */
|
|
|
|
|
@source '../../project-d/src/bar.html';
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project A is the current folder, but we explicitly configured
|
|
|
|
|
// `source(project-b)`, therefore project-a should not be included in
|
|
|
|
|
// the output.
|
|
|
|
|
'project-a/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-a/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project A explicitly includes an extension we usually ignore,
|
|
|
|
|
// therefore it should be included in the output.
|
|
|
|
|
'project-a/src/logo.jpg': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-a/src/logo.jpg']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project A explicitly includes node_modules/{my-lib-1,my-lib-2},
|
|
|
|
|
// therefore these files should be included in the output.
|
|
|
|
|
'project-a/node_modules/my-lib-1/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-a/node_modules/my-lib-1/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
'project-a/node_modules/my-lib-2/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-a/node_modules/my-lib-2/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project B is the configured `source(…)`, therefore auto source
|
|
|
|
|
// detection should include known extensions and folders in the output.
|
|
|
|
|
'project-b/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-b/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project B is the configured `source(…)`, therefore auto source
|
|
|
|
|
// detection should apply and node_modules should not be included in the
|
|
|
|
|
// output.
|
|
|
|
|
'project-b/node_modules/my-lib-3/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-b/node_modules/my-lib-3/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project C should apply auto source detection, therefore known
|
|
|
|
|
// extensions and folders should be included in the output.
|
|
|
|
|
'project-c/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-c/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project C should apply auto source detection, therefore known ignored
|
|
|
|
|
// extensions should not be included in the output.
|
|
|
|
|
'project-c/src/logo.jpg': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-c/src/logo.jpg']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project C should apply auto source detection, therefore node_modules
|
|
|
|
|
// should not be included in the output.
|
|
|
|
|
'project-c/node_modules/my-lib-1/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-c/node_modules/my-lib-1/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D should apply auto source detection rules, such as ignoring
|
|
|
|
|
// node_modules.
|
|
|
|
|
'project-d/node_modules/my-lib-1/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-d/node_modules/my-lib-1/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D has an explicit glob containing node_modules, thus should include the html file
|
|
|
|
|
'project-d/node_modules/my-lib-2/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-d/node_modules/my-lib-2/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'project-d/src/.gitignore': dedent`
|
|
|
|
|
foo.html
|
|
|
|
|
bar.html
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D, foo.html is ignored by the gitignore file.
|
|
|
|
|
'project-d/src/foo.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['SHOULD-NOT-EXIST-IN-OUTPUT'] content-['project-d/src/foo.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D, bar.html is ignored by the gitignore file. But explicitly
|
|
|
|
|
// listed as a `@source` glob.
|
|
|
|
|
'project-d/src/bar.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-d/src/bar.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D should look for files with the extensions html and js.
|
|
|
|
|
'project-d/src/index.html': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-d/src/index.html']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// Project D should have a binary file even though we ignore binary files
|
|
|
|
|
// by default, but it's explicitly listed.
|
|
|
|
|
'project-d/my-binary-file.bin': html`
|
|
|
|
|
<div
|
|
|
|
|
class="content-['project-d/my-binary-file.bin']"
|
|
|
|
|
></div>
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
},
|
Improve integration tests (stability + performance) (#15125)
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
2024-12-12 13:48:56 +01:00
|
|
|
async ({ fs, exec, spawn, root, expect }) => {
|
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
|
|
|
await exec('pnpm postcss src/index.css --output dist/out.css --verbose', {
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(`
|
|
|
|
|
"
|
|
|
|
|
--- ./project-a/dist/out.css ---
|
|
|
|
|
.content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-a/node modules/my-lib-1/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-a/node modules/my-lib-2/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] {
|
|
|
|
|
--tw-content: 'project-a/src/logo.jpg';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-b\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-b/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-c\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-c/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-d\\/my-binary-file\\.bin\\'\\] {
|
|
|
|
|
--tw-content: 'project-d/my-binary-file.bin';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-d\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-d/node modules/my-lib-2/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-d\\/src\\/bar\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-d/src/bar.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\'project-d\\/src\\/index\\.html\\'\\] {
|
|
|
|
|
--tw-content: 'project-d/src/index.html';
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
@property --tw-content {
|
|
|
|
|
syntax: "*";
|
|
|
|
|
inherits: false;
|
|
|
|
|
initial-value: "";
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
`)
|
|
|
|
|
|
|
|
|
|
// Watch mode tests
|
|
|
|
|
let process = await spawn(
|
|
|
|
|
'pnpm postcss src/index.css --output dist/out.css --watch --verbose',
|
|
|
|
|
{
|
|
|
|
|
cwd: path.join(root, 'project-a'),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
await process.onStderr((message) => message.includes('Waiting for file changes...'))
|
|
|
|
|
|
|
|
|
|
// Changes to project-a should not be included in the output, we changed the
|
|
|
|
|
// base folder to project-b.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/src/index.html',
|
|
|
|
|
html`<div class="[.changed_&]:content-['project-a/src/index.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileNotToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-a/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Changes to this file should be included, because we explicitly listed
|
|
|
|
|
// them using `@source`.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/src/logo.jpg',
|
|
|
|
|
html`<div class="[.changed_&]:content-['project-a/src/logo.jpg']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-a/src/logo.jpg']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Changes to these files should be included, because we explicitly listed
|
|
|
|
|
// them using `@source`.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/node_modules/my-lib-1/src/index.html',
|
|
|
|
|
html`<div
|
|
|
|
|
class="[.changed_&]:content-['project-a/node_modules/my-lib-1/src/index.html']"
|
|
|
|
|
></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-a/node_modules/my-lib-1/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-a/node_modules/my-lib-2/src/index.html',
|
|
|
|
|
html`<div
|
|
|
|
|
class="[.changed_&]:content-['project-a/node_modules/my-lib-2/src/index.html']"
|
|
|
|
|
></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-a/node_modules/my-lib-2/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Changes to this file should be included, because we changed the base to
|
|
|
|
|
// `project-b`.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-b/src/index.html',
|
|
|
|
|
html`<div class="[.changed_&]:content-['project-b/src/index.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-b/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Changes to this file should not be included. We did change the base to
|
|
|
|
|
// `project-b`, but we still apply the auto source detection rules which
|
|
|
|
|
// ignore `node_modules`.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-b/node_modules/my-lib-3/src/index.html',
|
|
|
|
|
html`<div
|
|
|
|
|
class="[.changed_&]:content-['project-b/node_modules/my-lib-3/src/index.html']"
|
|
|
|
|
></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileNotToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-b/node_modules/my-lib-3/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Project C was added explicitly via `@source`, therefore changes to these
|
|
|
|
|
// files should be included.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-c/src/index.html',
|
|
|
|
|
html`<div class="[.changed_&]:content-['project-c/src/index.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-c/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Except for these files, since they are ignored by the default auto source
|
|
|
|
|
// detection rules.
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-c/src/logo.jpg',
|
|
|
|
|
html`<div class="[.changed_&]:content-['project-c/src/logo.jpg']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileNotToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-c/src/logo.jpg']`,
|
|
|
|
|
])
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-c/node_modules/my-lib-1/src/index.html',
|
|
|
|
|
html`<div
|
|
|
|
|
class="[.changed_&]:content-['project-c/node_modules/my-lib-1/src/index.html']"
|
|
|
|
|
></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.expectFileNotToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.changed_&]:content-['project-c/node_modules/my-lib-1/src/index.html']`,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// Creating new files in the "root" of auto source detected folders
|
2024-10-30 10:56:55 -04:00
|
|
|
// We need to create the files and *then* update them because postcss-cli
|
|
|
|
|
// does not pick up new files — only changes to existing files.
|
|
|
|
|
await fs.create([
|
|
|
|
|
'project-b/new-file.html',
|
|
|
|
|
'project-b/new-folder/new-file.html',
|
|
|
|
|
'project-c/new-file.html',
|
|
|
|
|
'project-c/new-folder/new-file.html',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// If we don't wait writes will be coalesced into a "add" event which
|
|
|
|
|
// isn't picked up by postcss-cli.
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
|
|
|
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-b/new-file.html',
|
|
|
|
|
html`<div class="[.created_&]:content-['project-b/new-file.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-b/new-folder/new-file.html',
|
|
|
|
|
html`<div class="[.created_&]:content-['project-b/new-folder/new-file.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-c/new-file.html',
|
|
|
|
|
html`<div class="[.created_&]:content-['project-c/new-file.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
await fs.write(
|
|
|
|
|
'project-c/new-folder/new-file.html',
|
|
|
|
|
html`<div class="[.created_&]:content-['project-c/new-folder/new-file.html']"></div>`,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await fs.expectFileToContain('./project-a/dist/out.css', [
|
|
|
|
|
candidate`[.created_&]:content-['project-b/new-file.html']`,
|
|
|
|
|
candidate`[.created_&]:content-['project-b/new-folder/new-file.html']`,
|
|
|
|
|
candidate`[.created_&]:content-['project-c/new-file.html']`,
|
|
|
|
|
candidate`[.created_&]:content-['project-c/new-folder/new-file.html']`,
|
|
|
|
|
])
|
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
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'auto source detection disabled',
|
|
|
|
|
{
|
|
|
|
|
fs: {
|
|
|
|
|
'package.json': json`
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"postcss": "^8",
|
|
|
|
|
"postcss-cli": "^10",
|
|
|
|
|
"tailwindcss": "workspace:^",
|
|
|
|
|
"@tailwindcss/postcss": "workspace:^"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'postcss.config.js': js`
|
|
|
|
|
module.exports = {
|
|
|
|
|
plugins: {
|
|
|
|
|
'@tailwindcss/postcss': {},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
'index.css': css`
|
2025-02-25 11:36:43 +01:00
|
|
|
@reference 'tailwindcss/theme';
|
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
|
|
|
|
|
|
|
|
/* (1) */
|
|
|
|
|
/* - Only './src' should be auto-scanned, not the current working directory */
|
|
|
|
|
/* - .gitignore'd paths should be ignored (node_modules) */
|
|
|
|
|
/* - Binary extensions should be ignored (jpg, zip) */
|
|
|
|
|
@import 'tailwindcss/utilities' source(none);
|
|
|
|
|
|
|
|
|
|
/* (2) */
|
|
|
|
|
/* - './pages' should be auto-scanned */
|
|
|
|
|
/* - Only '.html' files should be included */
|
|
|
|
|
/* - './page/ignored.html' should be ignored */
|
|
|
|
|
@source "./pages/**/*.html";
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
'.gitignore': dedent`
|
|
|
|
|
/src/ignored
|
|
|
|
|
/pages/ignored.html
|
|
|
|
|
`,
|
|
|
|
|
|
|
|
|
|
// (1)
|
|
|
|
|
'index.html': 'content-["index.html"] content-["BAD"]', // "Root" source is in `./src`
|
|
|
|
|
'src/index.html': 'content-["src/index.html"] content-["BAD"]',
|
|
|
|
|
'src/nested/index.html': 'content-["src/nested/index.html"] content-["BAD"]',
|
|
|
|
|
'src/index.jpg': 'content-["src/index.jpg"] content-["BAD"]',
|
|
|
|
|
'src/nested/index.tar': 'content-["src/nested/index.tar"] content-["BAD"]',
|
|
|
|
|
'src/ignored/index.html': 'content-["src/ignored/index.html"] content-["BAD"]',
|
|
|
|
|
|
|
|
|
|
// (4)
|
|
|
|
|
'pages/foo.html': 'content-["pages/foo.html"]',
|
|
|
|
|
'pages/nested/foo.html': 'content-["pages/nested/foo.html"]',
|
|
|
|
|
'pages/ignored.html': 'content-["pages/ignored.html"] content-["BAD"]',
|
|
|
|
|
'pages/foo.jsx': 'content-["pages/foo.jsx"] content-["BAD"]',
|
|
|
|
|
'pages/nested/foo.jsx': 'content-["pages/nested/foo.jsx"] content-["BAD"]',
|
|
|
|
|
},
|
|
|
|
|
},
|
Improve integration tests (stability + performance) (#15125)
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
2024-12-12 13:48:56 +01:00
|
|
|
async ({ fs, exec, expect }) => {
|
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
|
|
|
await exec('pnpm postcss index.css --output dist/out.css')
|
|
|
|
|
|
|
|
|
|
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
|
|
|
|
|
"
|
|
|
|
|
--- ./dist/out.css ---
|
|
|
|
|
.content-\\[\\"pages\\/foo\\.html\\"\\] {
|
|
|
|
|
--tw-content: "pages/foo.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
.content-\\[\\"pages\\/nested\\/foo\\.html\\"\\] {
|
|
|
|
|
--tw-content: "pages/nested/foo.html";
|
|
|
|
|
content: var(--tw-content);
|
|
|
|
|
}
|
|
|
|
|
@property --tw-content {
|
|
|
|
|
syntax: "*";
|
|
|
|
|
inherits: false;
|
|
|
|
|
initial-value: "";
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
`)
|
|
|
|
|
},
|
|
|
|
|
)
|