tailwindcss/packages/@tailwindcss-vite/src/index.ts

403 lines
12 KiB
TypeScript
Raw Normal View History

Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
import {
compile,
env,
Features,
Instrumentation,
normalizePath,
optimize,
toSourceMap,
} from '@tailwindcss/node'
import { clearRequireCache } from '@tailwindcss/node/require-cache'
Improve Oxide scanner API (#14187) This PR updates the API for interacting with the Oxide API. Until now, we used the name `scanDir(…)` which is fine, but we do way more work right now. We now have features such as: 1. Auto source detection (can be turned off, e.g.: `@tailwindcss/vite` doesn't need it) 2. Scan based on `@source`s found in CSS files 3. Do "incremental" rebuilds (which means that the `scanDir(…)` result was stateful). To solve these issues, this PR introduces a new `Scanner` class where you can pass in the `detectSources` and `sources` options. E.g.: ```ts let scanner = new Scanner({ // Optional, omitting `detectSources` field disables automatic source detection detectSources: { base: __dirname }, // List of glob entries to scan. These come from `@source` directives in CSS. sources: [ { base: __dirname, pattern: "src/**/*.css" }, // … ], }); ``` The scanner object has the following API: ```ts export interface ChangedContent { /** File path to the changed file */ file?: string /** Contents of the changed file */ content?: string /** File extension */ extension: string } export interface DetectSources { /** Base path to start scanning from */ base: string } export interface GlobEntry { /** Base path of the glob */ base: string /** Glob pattern */ pattern: string } export interface ScannerOptions { /** Automatically detect sources in the base path */ detectSources?: DetectSources /** Glob sources */ sources?: Array<GlobEntry> } export declare class Scanner { constructor(opts: ScannerOptions) scan(): Array<string> scanFiles(input: Array<ChangedContent>): Array<string> get files(): Array<string> get globs(): Array<GlobEntry> } ``` The `scanFiles(…)` method is used for incremental rebuilds. It takes the `ChangedContent` array for all the new/changes files. It returns whether we scanned any new candidates or not. Note that the `scanner` object is stateful, this means that we don't have to track candidates in a `Set` anymore. We can just call `getCandidates()` when we need it. This PR also removed some unused code that we had in the `scanDir(…)` function to allow for sequential or parallel `IO`, and sequential or parallel `Parsing`. We only used the same `IO` and `Parsing` strategies for all files, so I just got rid of it. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:05:42 +02:00
import { Scanner } from '@tailwindcss/oxide'
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 fs from 'node:fs/promises'
import path from 'node:path'
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
2024-03-05 14:23:26 +01:00
const DEBUG = env.DEBUG
const SPECIAL_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)\b/
const COMMON_JS_PROXY_RE = /\?commonjs-proxy/
const INLINE_STYLE_ID_RE = /[?&]index\=\d+\.css$/
export type PluginOptions = {
/**
* Optimize and minify the output CSS.
*/
optimize?: boolean | { minify?: boolean }
}
export default function tailwindcss(opts: PluginOptions = {}): Plugin[] {
let servers: ViteDevServer[] = []
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
let config: ResolvedConfig | null = null
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
let isSSR = false
let shouldOptimize = true
let minify = true
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
let roots: DefaultMap<string, Root> = new DefaultMap((id) => {
let cssResolver = config!.createResolver({
...config!.resolve,
extensions: ['.css'],
mainFields: ['style'],
conditions: ['style', 'development|production'],
tryIndex: false,
preferRelative: true,
})
function customCssResolver(id: string, base: string) {
return cssResolver(id, base, true, isSSR)
}
let jsResolver = config!.createResolver(config!.resolve)
function customJsResolver(id: string, base: string) {
return jsResolver(id, base, true, isSSR)
}
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
return new Root(
id,
config!.root,
// Currently, Vite only supports CSS source maps in development and they
// are off by default. Check to see if we need them or not.
config?.css.devSourcemap ?? false,
customCssResolver,
customJsResolver,
)
})
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
2024-03-05 14:23:26 +01:00
return [
{
// Step 1: Scan source files for candidates
name: '@tailwindcss/vite:scan',
enforce: 'pre',
configureServer(server) {
servers.push(server)
2024-03-05 14:23:26 +01:00
},
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
async configResolved(_config) {
config = _config
isSSR = config.build.ssr !== false && config.build.ssr !== undefined
// By default we optimize CSS during the build phase but if the user
// provides explicit options we'll use those instead
if (opts.optimize !== undefined) {
shouldOptimize = opts.optimize !== false
}
// Minification is also performed when optimizing as long as it's also
// enabled in Vite
minify = shouldOptimize && config.build.cssMinify !== false
// But again, the user can override that choice explicitly
if (typeof opts.optimize === 'object') {
minify = opts.optimize.minify !== false
}
2024-03-05 14:23:26 +01:00
},
},
{
// Step 2 (serve mode): Generate CSS
2024-03-05 14:23:26 +01:00
name: '@tailwindcss/vite:generate:serve',
apply: 'serve',
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
enforce: 'pre',
2024-03-20 16:40:50 -04:00
async transform(src, id, options) {
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
if (!isPotentialCssRootFile(id)) return
2024-03-05 14:23:26 +01:00
using I = new Instrumentation()
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.start('[@tailwindcss/vite] Generate CSS (serve)')
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
let root = roots.get(id)
2024-03-05 14:23:26 +01:00
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
let result = await root.generate(src, (file) => this.addWatchFile(file), I)
if (!result) {
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
roots.delete(id)
return src
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.end('[@tailwindcss/vite] Generate CSS (serve)')
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
return result
2024-03-05 14:23:26 +01:00
},
},
{
// Step 2 (full build): Generate CSS
name: '@tailwindcss/vite:generate:build',
apply: 'build',
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
enforce: 'pre',
async transform(src, id) {
if (!isPotentialCssRootFile(id)) return
using I = new Instrumentation()
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.start('[@tailwindcss/vite] Generate CSS (build)')
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
let root = roots.get(id)
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
let result = await root.generate(src, (file) => this.addWatchFile(file), I)
if (!result) {
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
roots.delete(id)
return src
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.end('[@tailwindcss/vite] Generate CSS (build)')
if (shouldOptimize) {
DEBUG && I.start('[@tailwindcss/vite] Optimize CSS')
result = optimize(result.code, {
minify,
map: result.map,
})
DEBUG && I.end('[@tailwindcss/vite] Optimize CSS')
}
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
return result
2024-03-05 14:23:26 +01:00
},
},
] satisfies Plugin[]
}
function getExtension(id: string) {
let [filename] = id.split('?', 2)
return path.extname(filename).slice(1)
}
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
function isPotentialCssRootFile(id: string) {
if (id.includes('/.vite/')) return
let extension = getExtension(id)
let isCssFile =
(extension === 'css' || id.includes('&lang.css') || id.match(INLINE_STYLE_ID_RE)) &&
// Don't intercept special static asset resources
!SPECIAL_QUERY_RE.test(id) &&
!COMMON_JS_PROXY_RE.test(id)
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
return isCssFile
}
function idToPath(id: string) {
return path.resolve(id.replace(/\?.*$/, ''))
}
/**
* A Map that can generate default values for keys that don't exist.
* Generated default values are added to the map to avoid recomputation.
*/
class DefaultMap<K, V> extends Map<K, V> {
constructor(private factory: (key: K, self: DefaultMap<K, V>) => V) {
super()
}
get(key: K): V {
let value = super.get(key)
if (value === undefined) {
value = this.factory(key, this)
this.set(key, value)
}
return value
}
}
class Root {
// The lazily-initialized Tailwind compiler components. These are persisted
// throughout rebuilds but will be re-initialized if the rebuild strategy is
// set to `full`.
private compiler?: Awaited<ReturnType<typeof compile>>
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
// The lazily-initialized Tailwind scanner.
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
private scanner?: Scanner
// List of all candidates that were being returned by the root scanner during
// the lifetime of the root.
private candidates: Set<string> = new Set<string>()
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
// List of all build dependencies (e.g. imported stylesheets or plugins) and
// their last modification timestamp. If no mtime can be found, we need to
// assume the file has always changed.
private buildDependencies = new Map<string, number | null>()
Vite: Don't track candidate changes for Svelte `<style>` tags (#14981) Closes #14965 This PR changes the way we register Tailwind CSS as a Svelte preprocessor when using the Vite plugin. The idea is to reduce the bookkeeping for interacting with CSS inside `<style>` tags so that we have a more consistent behavior and make sure the Svelte-specific post-processing (e.g. local class mangling) works as expected. Prior to this change, we were running Tailwind CSS as a Svelte preprocessor and then we would transform the file again when necessary inside the Vite `transform` hook. This is necessary to have the right list of candidates when we build the final CSS, but it did cause some situation to not apply the Svelte post-processors anymore. The repro for this seemed to indicate a timing specific issue and I did notice that specifically the code where we invalidate modules in Vite would cause unexpected processing orders. We do, however, not officially support rendering utilities (`@tailwind utilities;`) inside `<style>` tag. This is because the `<style>` block is scoped by default and emitting utilities will always include utilities for all classes in your whole project. For this case, we highly recommend creating as separate `.css` file and importing it explicitly. With this limitation in place, the additional bookkeeping where we need to invalidate modules because the candidate list has changed is no longer necessary and removing it allows us to reduce the complexity of the Svelte integration. ## Test Plan https://github.com/user-attachments/assets/32c8e91f-ab21-48c6-aeaf-2582273b9bac Not seen in the test plan above I also tested the `pnpm build --watch` step of the Vite project. This does require the `pnpm preview` server to restart but the build artifact are updated as expected.
2024-11-13 16:42:49 +01:00
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
constructor(
private id: string,
private base: string,
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
private enableSourceMaps: boolean,
private customCssResolver: (id: string, base: string) => Promise<string | false | undefined>,
private customJsResolver: (id: string, base: string) => Promise<string | false | undefined>,
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
) {}
// Generate the CSS for the root file. This can return false if the file is
// not considered a Tailwind root. When this happened, the root can be GCed.
public async generate(
content: string,
_addWatchFile: (file: string) => void,
I: Instrumentation,
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
): Promise<
| {
code: string
map: string | undefined
}
| false
> {
let inputPath = idToPath(this.id)
function addWatchFile(file: string) {
// Don't watch the input file since it's already a dependency anc causes
// issues with some setups (e.g. Qwik).
if (file === inputPath) {
return
}
// Scanning `.svg` file containing a `#` or `?` in the path will
// crash Vite. We work around this for now by ignoring updates to them.
//
// https://github.com/tailwindlabs/tailwindcss/issues/16877
if (/[\#\?].*\.svg$/.test(file)) {
return
}
_addWatchFile(file)
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
let requiresBuildPromise = this.requiresBuild()
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
let inputBase = path.dirname(path.resolve(inputPath))
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
if (!this.compiler || !this.scanner || (await requiresBuildPromise)) {
clearRequireCache(Array.from(this.buildDependencies.keys()))
this.buildDependencies.clear()
this.addBuildDependency(idToPath(inputPath))
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
DEBUG && I.start('Setup compiler')
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
let addBuildDependenciesPromises: Promise<void>[] = []
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
this.compiler = await compile(content, {
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
from: this.enableSourceMaps ? this.id : undefined,
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
base: inputBase,
shouldRewriteUrls: true,
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
onDependency: (path) => {
addWatchFile(path)
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
addBuildDependenciesPromises.push(this.addBuildDependency(path))
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
},
customCssResolver: this.customCssResolver,
customJsResolver: this.customJsResolver,
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
})
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
await Promise.all(addBuildDependenciesPromises)
DEBUG && I.end('Setup compiler')
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
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.start('Setup scanner')
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
let sources = (() => {
// Disable auto source detection
if (this.compiler.root === 'none') {
return []
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
// No root specified, auto-detect based on the `**/*` pattern
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (this.compiler.root === null) {
Add `@source not` support (#17255) This PR adds a new source detection feature: `@source not "…"`. It can be used to exclude files specifically from your source configuration without having to think about creating a rule that matches all but the requested file: ```css @import "tailwindcss"; @source not "../src/my-tailwind-js-plugin.js"; ``` While working on this feature, we noticed that there are multiple places with different heuristics we used to scan the file system. These are: - Auto source detection (so the default configuration or an `@source "./my-dir"`) - Custom sources ( e.g. `@source "./**/*.bin"` — these contain file extensions) - The code to detect updates on the file system Because of the different heuristics, we were able to construct failing cases (e.g. when you create a new file into `my-dir` that would be thrown out by auto-source detection, it'd would actually be scanned). We were also leaving a lot of performance on the table as the file system is traversed multiple times for certain problems. To resolve these issues, we're now unifying all of these systems into one `ignore` crate walker setup. We also implemented features like auto-source-detection and the `not` flag as additional _gitignore_ rules only, avoid the need for a lot of custom code needed to make decisions. High level, this is what happens after the now: - We collect all non-negative `@source` rules into a list of _roots_ (that is the source directory for this rule) and optional _globs_ (that is the actual rules for files in this file). For custom sources (i.e with a custom `glob`), we add an allowlist rule to the gitignore setup, so that we can be sure these files are always included. - For every negative `@source` rule, we create respective ignore rules. - Furthermore we have a custom filter that ensures files are only read if they have been changed since the last time they were read. So, consider the following setup: ```css /* packages/web/src/index.css */ @import "tailwindcss"; @source "../../lib/ui/**/*.bin"; @source not "../../lib/ui/expensive.bin"; ``` This creates a git ignore file that (simplified) looks like this: ```gitignore # Auto-source rules *.{exe,node,bin,…} *.{css,scss,sass,…} {node_modules,git}/ # Custom sources can overwrite auto-source rules !lib/ui/**/*.bin # Negative rules lib/ui/expensive.bin ``` We then use this information _on top of your existing `.gitignore` setup_ to resolve files (i.e so if your `.gitignore` contains rules e.g. `dist/` this line is going to be added _before_ any of the rules lined out in the example above. This allows negative rules to allow-list your `.gitignore` rules. To implement this, we're rely on the `ignore` crate but we had to make various changes, very specific, to it so we decided to fork the crate. All changes are prefixed with a `// CHANGED:` block but here are the most-important ones: - We added a way to add custom ignore rules that _extend_ (rather than overwrite) your existing `.gitignore` rules - We updated the order in which files are resolved and made it so that more-specific files can allow-list more generic ignore rules. - We resolved various issues related to adding more than one base path to the traversal and ensured it works consistent for Linux, macOS, and Windows. ## Behavioral changes 1. Any custom glob defined via `@source` now wins over your `.gitignore` file and the auto-content rules. - Resolves #16920 3. The `node_modules` and `.git` folders as well as the `.gitignore` file are now ignored by default (but can be overridden by an explicit `@source` rule). - Resolves #17318 - Resolves #15882 4. Source paths into ignored-by-default folders (like `node_modules`) now also win over your `.gitignore` configuration and auto-content rules. - Resolves #16669 5. Introduced `@source not "…"` to negate any previous rules. - Resolves #17058 6. Negative `content` rules in your legacy JavaScript configuration (e.g. `content: ['!./src']`) now work with v4. - Resolves #15943 7. The order of `@source` definitions matter now, because you can technically include or negate previous rules. This is similar to your `.gitingore` file. 9. Rebuilds in watch mode now take the `@source` configuration into account - Resolves #15684 ## Combining with other features Note that the `not` flag is also already compatible with [`@source inline(…)`](https://github.com/tailwindlabs/tailwindcss/pull/17147) added in an earlier commit: ```css @import "tailwindcss"; @source not inline("container"); ``` ## Test plan - We added a bunch of oxide unit tests to ensure that the right files are scanned - We updated the existing integration tests with new `@source not "…"` specific examples and updated the existing tests to match the subtle behavior changes - We also added a new special tag `[ci-all]` that, when added to the description of a PR, causes the PR to run unit and integration tests on all operating systems. [ci-all] --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-25 15:54:41 +01:00
return [{ base: this.base, pattern: '**/*', negated: false }]
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
}
// Use the specified root
Add `@source not` support (#17255) This PR adds a new source detection feature: `@source not "…"`. It can be used to exclude files specifically from your source configuration without having to think about creating a rule that matches all but the requested file: ```css @import "tailwindcss"; @source not "../src/my-tailwind-js-plugin.js"; ``` While working on this feature, we noticed that there are multiple places with different heuristics we used to scan the file system. These are: - Auto source detection (so the default configuration or an `@source "./my-dir"`) - Custom sources ( e.g. `@source "./**/*.bin"` — these contain file extensions) - The code to detect updates on the file system Because of the different heuristics, we were able to construct failing cases (e.g. when you create a new file into `my-dir` that would be thrown out by auto-source detection, it'd would actually be scanned). We were also leaving a lot of performance on the table as the file system is traversed multiple times for certain problems. To resolve these issues, we're now unifying all of these systems into one `ignore` crate walker setup. We also implemented features like auto-source-detection and the `not` flag as additional _gitignore_ rules only, avoid the need for a lot of custom code needed to make decisions. High level, this is what happens after the now: - We collect all non-negative `@source` rules into a list of _roots_ (that is the source directory for this rule) and optional _globs_ (that is the actual rules for files in this file). For custom sources (i.e with a custom `glob`), we add an allowlist rule to the gitignore setup, so that we can be sure these files are always included. - For every negative `@source` rule, we create respective ignore rules. - Furthermore we have a custom filter that ensures files are only read if they have been changed since the last time they were read. So, consider the following setup: ```css /* packages/web/src/index.css */ @import "tailwindcss"; @source "../../lib/ui/**/*.bin"; @source not "../../lib/ui/expensive.bin"; ``` This creates a git ignore file that (simplified) looks like this: ```gitignore # Auto-source rules *.{exe,node,bin,…} *.{css,scss,sass,…} {node_modules,git}/ # Custom sources can overwrite auto-source rules !lib/ui/**/*.bin # Negative rules lib/ui/expensive.bin ``` We then use this information _on top of your existing `.gitignore` setup_ to resolve files (i.e so if your `.gitignore` contains rules e.g. `dist/` this line is going to be added _before_ any of the rules lined out in the example above. This allows negative rules to allow-list your `.gitignore` rules. To implement this, we're rely on the `ignore` crate but we had to make various changes, very specific, to it so we decided to fork the crate. All changes are prefixed with a `// CHANGED:` block but here are the most-important ones: - We added a way to add custom ignore rules that _extend_ (rather than overwrite) your existing `.gitignore` rules - We updated the order in which files are resolved and made it so that more-specific files can allow-list more generic ignore rules. - We resolved various issues related to adding more than one base path to the traversal and ensured it works consistent for Linux, macOS, and Windows. ## Behavioral changes 1. Any custom glob defined via `@source` now wins over your `.gitignore` file and the auto-content rules. - Resolves #16920 3. The `node_modules` and `.git` folders as well as the `.gitignore` file are now ignored by default (but can be overridden by an explicit `@source` rule). - Resolves #17318 - Resolves #15882 4. Source paths into ignored-by-default folders (like `node_modules`) now also win over your `.gitignore` configuration and auto-content rules. - Resolves #16669 5. Introduced `@source not "…"` to negate any previous rules. - Resolves #17058 6. Negative `content` rules in your legacy JavaScript configuration (e.g. `content: ['!./src']`) now work with v4. - Resolves #15943 7. The order of `@source` definitions matter now, because you can technically include or negate previous rules. This is similar to your `.gitingore` file. 9. Rebuilds in watch mode now take the `@source` configuration into account - Resolves #15684 ## Combining with other features Note that the `not` flag is also already compatible with [`@source inline(…)`](https://github.com/tailwindlabs/tailwindcss/pull/17147) added in an earlier commit: ```css @import "tailwindcss"; @source not inline("container"); ``` ## Test plan - We added a bunch of oxide unit tests to ensure that the right files are scanned - We updated the existing integration tests with new `@source not "…"` specific examples and updated the existing tests to match the subtle behavior changes - We also added a new special tag `[ci-all]` that, when added to the description of a PR, causes the PR to run unit and integration tests on all operating systems. [ci-all] --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-25 15:54:41 +01:00
return [{ ...this.compiler.root, negated: false }]
})().concat(this.compiler.sources)
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
this.scanner = new Scanner({ sources })
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
DEBUG && I.end('Setup scanner')
} else {
for (let buildDependency of this.buildDependencies.keys()) {
addWatchFile(buildDependency)
}
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
}
if (
!(
this.compiler.features &
(Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities)
)
) {
return false
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
if (this.compiler.features & Features.Utilities) {
Vite: Don't track candidate changes for Svelte `<style>` tags (#14981) Closes #14965 This PR changes the way we register Tailwind CSS as a Svelte preprocessor when using the Vite plugin. The idea is to reduce the bookkeeping for interacting with CSS inside `<style>` tags so that we have a more consistent behavior and make sure the Svelte-specific post-processing (e.g. local class mangling) works as expected. Prior to this change, we were running Tailwind CSS as a Svelte preprocessor and then we would transform the file again when necessary inside the Vite `transform` hook. This is necessary to have the right list of candidates when we build the final CSS, but it did cause some situation to not apply the Svelte post-processors anymore. The repro for this seemed to indicate a timing specific issue and I did notice that specifically the code where we invalidate modules in Vite would cause unexpected processing orders. We do, however, not officially support rendering utilities (`@tailwind utilities;`) inside `<style>` tag. This is because the `<style>` block is scoped by default and emitting utilities will always include utilities for all classes in your whole project. For this case, we highly recommend creating as separate `.css` file and importing it explicitly. With this limitation in place, the additional bookkeeping where we need to invalidate modules because the candidate list has changed is no longer necessary and removing it allows us to reduce the complexity of the Svelte integration. ## Test Plan https://github.com/user-attachments/assets/32c8e91f-ab21-48c6-aeaf-2582273b9bac Not seen in the test plan above I also tested the `pnpm build --watch` step of the Vite project. This does require the `pnpm preview` server to restart but the build artifact are updated as expected.
2024-11-13 16:42:49 +01:00
// This should not be here, but right now the Vite plugin is setup where we
// setup a new scanner and compiler every time we request the CSS file
// (regardless whether it actually changed or not).
DEBUG && I.start('Scan for candidates')
Vite: Don't track candidate changes for Svelte `<style>` tags (#14981) Closes #14965 This PR changes the way we register Tailwind CSS as a Svelte preprocessor when using the Vite plugin. The idea is to reduce the bookkeeping for interacting with CSS inside `<style>` tags so that we have a more consistent behavior and make sure the Svelte-specific post-processing (e.g. local class mangling) works as expected. Prior to this change, we were running Tailwind CSS as a Svelte preprocessor and then we would transform the file again when necessary inside the Vite `transform` hook. This is necessary to have the right list of candidates when we build the final CSS, but it did cause some situation to not apply the Svelte post-processors anymore. The repro for this seemed to indicate a timing specific issue and I did notice that specifically the code where we invalidate modules in Vite would cause unexpected processing orders. We do, however, not officially support rendering utilities (`@tailwind utilities;`) inside `<style>` tag. This is because the `<style>` block is scoped by default and emitting utilities will always include utilities for all classes in your whole project. For this case, we highly recommend creating as separate `.css` file and importing it explicitly. With this limitation in place, the additional bookkeeping where we need to invalidate modules because the candidate list has changed is no longer necessary and removing it allows us to reduce the complexity of the Svelte integration. ## Test Plan https://github.com/user-attachments/assets/32c8e91f-ab21-48c6-aeaf-2582273b9bac Not seen in the test plan above I also tested the `pnpm build --watch` step of the Vite project. This does require the `pnpm preview` server to restart but the build artifact are updated as expected.
2024-11-13 16:42:49 +01:00
for (let candidate of this.scanner.scan()) {
this.candidates.add(candidate)
}
DEBUG && I.end('Scan for candidates')
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269) Fixes #14205 Fixes #14106 This PR reworks the Vite extension in order to supprt `lightningcss` as the pre-processor, enable faster rebuilds, and adds support for `vite build --watch` mode. To make this change possible, we've done two major changes to the extension that have caused the other changes. ## 1. Tailwind CSS is a preprocessor We now run all of our modifications in `enforce: 'pre'`. This means that Tailwind CSS now gets the untransformed CSS files rather than the one already going through postcss or lightningcss. We do this because Tailwind CSS _is_ a preprocessor at the same level as those tools and we do sometimes use the language in ways that [creates problems when it's the input for other bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269). The correct solution here is to make Tailwind not depend on any other transformations. The main reason we were not using the `enforce: 'pre'` phase in Vite before was becuase we relied on the `@import` flattening of postcss so we now have to do this manually. `@import` flattening is now a concern that every Tailwind V4 client has to deal with so this might actually be something we want to inline into tailwindcss in the future. ## 2. A Vite config can have multiple Tailwind roots This is something that we have not made very explicit in the previous iteration of the Vite plugin but we have to support multiple Tailwind roots in a single Vite workspace. A Tailwind root is a CSS file that is used to configure Tailwind. Technically, any CSS file can be the input for `tailwindcss` but you have to add certain rules (e.g. `@import "tailwindcss";`) to make the compiler do something. A workspace can have multiple of these rules (e.g. by having different Tailwind configures for different sub-pages). With the addition of [support for `@source` rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS config files](https://github.com/tailwindlabs/tailwindcss/pull/14239), Tailwind roots become more complex and can have a custom list of _dependencies_ (that is other JavaScript modules the compiler includes as part of these new rules). In order to _only rebuild the roots we need to_, we have to make this separation very clear. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
}
if (this.compiler.features & Features.Utilities) {
// Watch individual files found via custom `@source` paths
for (let file of this.scanner.files) {
addWatchFile(file)
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
}
// Watch globs found via custom `@source` paths
for (let glob of this.scanner.globs) {
if (glob.pattern[0] === '!') continue
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
let relative = path.relative(this.base, glob.base)
if (relative[0] !== '.') {
relative = './' + relative
}
// Ensure relative is a posix style path since we will merge it with the
// glob.
relative = normalizePath(relative)
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
addWatchFile(path.posix.join(relative, glob.pattern))
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
let root = this.compiler.root
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
if (root !== 'none' && root !== null) {
let basePath = normalizePath(path.resolve(root.base, root.pattern))
let isDir = await fs.stat(basePath).then(
(stats) => stats.isDirectory(),
() => false,
Auto source detection improvements (#14820) This PR introduces a new `source(…)` argument and improves on the existing `@source`. The goal of this PR is to make the automatic source detection configurable, let's dig in. By default, we will perform automatic source detection starting at the current working directory. Auto source detection will find plain text files (no binaries, images, ...) and will ignore git-ignored files. If you want to start from a different directory, you can use the new `source(…)` next to the `@import "tailwindcss/utilities" layer(utilities) source(…)`. E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss/utilities' layer(utilities) source('../../'); ``` Most people won't split their source files, and will just use the simple `@import "tailwindcss";`, because of this reason, you can use `source(…)` on the import as well: E.g.: ```css /* ./src/styles/index.css */ @import 'tailwindcss' source('../../'); ``` Sometimes, you want to rely on auto source detection, but also want to look in another directory for source files. In this case, yuo can use the `@source` directive: ```css /* ./src/index.css */ @import 'tailwindcss'; /* Look for `blade.php` files in `../resources/views` */ @source '../resources/views/**/*.blade.php'; ``` However, you don't need to specify the extension, instead you can just point the directory and all the same automatic source detection rules will apply. ```css /* ./src/index.css */ @import 'tailwindcss'; @source '../resources/views'; ``` If, for whatever reason, you want to disable the default source detection feature entirely, and only want to rely on very specific glob patterns you define, then you can disable it via `source(none)`. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Only look at .blade.php files, nothing else */ @source "../resources/views/**/*.blade.php"; ``` Note: even with `source(none)`, if your `@source` points to a directory, then auto source detection will still be performed in that directory. If you don't want that, then you can simply add explicit files in the globs as seen in the previous example. ```css /* Completely disable the default auto source detection */ @import 'tailwindcss' source(none); /* Run auto source detection in `../resources/views` */ @source "../resources/views"; ``` --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 21:33:34 +01:00
)
if (!isDir) {
throw new Error(
`The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
)
}
}
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
}
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
}
DEBUG && I.start('Build CSS')
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
let code = this.compiler.build([...this.candidates])
DEBUG && I.end('Build CSS')
Add support for source maps (#17775) Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file)
2025-05-08 16:29:49 -04:00
DEBUG && I.start('Build Source Map')
let map = this.enableSourceMaps ? toSourceMap(this.compiler.buildSourceMap()).raw : undefined
DEBUG && I.end('Build Source Map')
return {
code,
map,
}
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
}
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
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
private async addBuildDependency(path: string) {
let mtime: number | null = null
try {
mtime = (await fs.stat(path)).mtimeMs
} catch {}
this.buildDependencies.set(path, mtime)
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01: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
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
private async requiresBuild(): Promise<boolean> {
for (let [path, mtime] of this.buildDependencies) {
if (mtime === null) return true
try {
let stat = await fs.stat(path)
if (stat.mtimeMs > mtime) {
return true
}
} catch {
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
return true
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
}
}
Vite: Remove module-graph scanner (#16631) Alternative to #16425 Fixes #16585 Fixes #16389 Fixes #16252 Fixes #15794 Fixes #16646 Fixes #16358 This PR changes the Vite plugin to use the file-system to discover potential class names instead of relying on the module-graph. This comes after a lot of testing and various issue reports where builds that span different Vite instances were missing class names. Because we now scan for candidates using the file-system, we can also remove a lot of the bookkeeping necessary to make production builds and development builds work as we no longer have to change the resulting stylesheet based on the `transform` callbacks of other files that might happen later. This change comes at a small performance penalty that is noticeable especially on very large projects with many files to scan. However, we offset that change by fixing an issue that I found in the current Vite integration that did a needless rebuild of the whole Tailwind root whenever any source file changed. Because of how impactful this change is, I expect many normal to medium sized projects to actually see a performance improvement after these changes. Furthermore we do plan to continue to use the module-graph to further improve the performance in dev mode. ## Test plan - Added new integration tests with cases found across the issues above. - Manual testing by adding a local version of the Vite plugin to repos from the issue list above and the [tailwindcss playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
return false
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
}
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
}