From e9ac79e714e5a43c86865cb86bfa19a29848660f Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 25 Feb 2025 11:11:36 -0500
Subject: [PATCH 001/108] Update third party software notices
---
.../ThirdPartyNotices.txt | 649 ++++++++++--------
1 file changed, 362 insertions(+), 287 deletions(-)
diff --git a/packages/tailwindcss-language-server/ThirdPartyNotices.txt b/packages/tailwindcss-language-server/ThirdPartyNotices.txt
index 26d8efa8..ee6b7439 100644
--- a/packages/tailwindcss-language-server/ThirdPartyNotices.txt
+++ b/packages/tailwindcss-language-server/ThirdPartyNotices.txt
@@ -1,3 +1,78 @@
+@csstools/css-parser-algorithms@2.1.1
+
+The MIT License (MIT)
+
+Copyright 2022 Romain Menke, Antonio Laguna
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
+@csstools/css-tokenizer@2.1.1
+
+The MIT License (MIT)
+
+Copyright 2022 Romain Menke, Antonio Laguna
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
+@csstools/media-query-list-parser@2.0.4
+
+The MIT License (MIT)
+
+Copyright 2022 Romain Menke, Antonio Laguna
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
@parcel/watcher@2.0.3
MIT License
@@ -343,7 +418,33 @@ SOFTWARE.
================================================================================
-chokidar@3.5.1
+braces@3.0.3
+
+The MIT License (MIT)
+
+Copyright (c) 2014-present, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+================================================================================
+
+chokidar@3.6.0
The MIT License (MIT)
@@ -382,29 +483,28 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
-culori@0.20.1
-
-MIT License
+css.escape@1.5.1
-Copyright (c) 2018 Dan Burzo
+Copyright Mathias Bynens
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================================================
@@ -507,6 +607,20 @@ THE SOFTWARE.
================================================================================
+detect-indent@6.0.0
+
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
dlv@1.1.3
# `dlv(obj, keypath)` [](https://npmjs.com/package/dlv) [](https://travis-ci.org/developit/dlv)
@@ -588,7 +702,7 @@ delve(obj, undefined, 'foo') === 'foo';
================================================================================
-dset@3.1.2
+dset@3.1.4
The MIT License (MIT)
@@ -614,31 +728,6 @@ THE SOFTWARE.
================================================================================
-enhanced-resolve@5.15.0
-
-Copyright JS Foundation and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-================================================================================
-
fast-glob@3.2.4
The MIT License (MIT)
@@ -679,11 +768,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
-is-builtin-module@3.2.1
+klona@2.0.4
MIT License
-Copyright (c) Sindre Sorhus (sindresorhus.com)
+Copyright (c) Luke Edwards (lukeed.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -693,37 +782,61 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
-klona@2.0.4
-
-MIT License
+line-column@1.0.2
-Copyright (c) Luke Edwards (lukeed.com)
+Copyright (c) 2016 IRIDE Monad
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
================================================================================
-minimatch@5.1.4
+moo@0.5.1
-The ISC License
+BSD 3-Clause License
-Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors
+Copyright (c) 2017, Tim Radvan (tjvr)
+All rights reserved.
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
-IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
@@ -819,7 +932,34 @@ OTHER DEALINGS IN THE SOFTWARE.
================================================================================
-postcss@8.3.9
+postcss-value-parser@4.2.0
+
+Copyright (c) Bogdan Chadkin
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
+postcss@8.4.31
The MIT License (MIT)
@@ -870,15 +1010,89 @@ SOFTWARE.
================================================================================
-stack-trace@0.0.10
+semver@7.7.1
-Copyright (c) 2011 Felix Geisendörfer (felix@debuggable.com)
+The ISC License
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+================================================================================
+
+sift-string@0.0.2
+
+# sift
+
+ Fast String Distance (SIFT) Algorithm.
+
+[](https://nodei.co/npm/sift-string/)
+
+[](https://david-dm.org/timoxley/sift)
+
+## Installation
+
+#### Browserify/Node
+
+ $ npm install sift-string
+
+
+#### Component
+
+ $ component install timoxley/sift
+
+## Demo
+
+[Demo](http://timoxley.github.com/sift/examples/spellcheck/)
+
+or if you want to check it out locally:
+
+```bash
+# run only once to install npm dev dependencies
+npm install .
+# this will install && build the components and open the demo web page
+npm run c-demo
+```
+
+## API
+
+### sift(string, string)
+
+Return 'distance' between two strings.
+
+## TODO
+
+* Dictionary Helper supply it emits suggestions.
+
+## Credit
+
+Code extracted from [MailCheck](https://github.com/kicksend/mailcheck)
+
+## License
+
+ MIT
+
+================================================================================
+
+stack-trace@0.0.10
+
+Copyright (c) 2011 Felix Geisendörfer (felix@debuggable.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
@@ -894,7 +1108,34 @@ Copyright (c) 2011 Felix Geisendörfer (felix@debuggable.com)
================================================================================
-tailwindcss@3.3.0
+stringify-object@3.3.0
+
+Copyright (c) 2015, Yeoman team
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+================================================================================
+
+tailwindcss@3.4.17
MIT License
@@ -920,7 +1161,33 @@ SOFTWARE.
================================================================================
-vscode-css-languageservice@5.4.1
+tmp-cache@1.1.0
+
+The MIT License (MIT)
+
+Copyright (c) Luke Edwards (lukeed.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+================================================================================
+
+vscode-css-languageservice@6.2.9
The MIT License (MIT)
@@ -946,7 +1213,7 @@ SOFTWARE.
================================================================================
-vscode-languageserver-textdocument@1.0.7
+vscode-jsonrpc@8.2.0
Copyright (c) Microsoft Corporation
@@ -962,7 +1229,7 @@ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
-vscode-languageserver@8.0.2
+vscode-languageclient@8.1.0
Copyright (c) Microsoft Corporation
@@ -978,238 +1245,46 @@ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
-vscode-uri@3.0.2
-
-The MIT License (MIT)
-
-Copyright (c) Microsoft
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+vscode-languageserver-textdocument@1.0.11
-================================================================================
-
-css.escape@1.5.1
-
-Copyright Mathias Bynens
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-================================================================================
+Copyright (c) Microsoft Corporation
-detect-indent@6.0.0
+All rights reserved.
MIT License
-Copyright (c) Sindre Sorhus (sindresorhus.com)
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-================================================================================
-
-line-column@1.0.2
-
-Copyright (c) 2016 IRIDE Monad
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================================================
-moo@0.5.1
+vscode-languageserver@8.1.0
-BSD 3-Clause License
+Copyright (c) Microsoft Corporation
-Copyright (c) 2017, Tim Radvan (tjvr)
All rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-================================================================================
-
-semver@7.3.7
-
-The ISC License
-
-Copyright (c) Isaac Z. Schlueter and Contributors
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
-IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-================================================================================
-
-sift-string@0.0.2
-
-# sift
-
- Fast String Distance (SIFT) Algorithm.
-
-[](https://nodei.co/npm/sift-string/)
-
-[](https://david-dm.org/timoxley/sift)
-
-## Installation
-
-#### Browserify/Node
-
- $ npm install sift-string
-
-
-#### Component
-
- $ component install timoxley/sift
-
-## Demo
-
-[Demo](http://timoxley.github.com/sift/examples/spellcheck/)
-
-or if you want to check it out locally:
-
-```bash
-# run only once to install npm dev dependencies
-npm install .
-# this will install && build the components and open the demo web page
-npm run c-demo
-```
-
-## API
-
-### sift(string, string)
-
-Return 'distance' between two strings.
-
-## TODO
-
-* Dictionary Helper supply it emits suggestions.
-
-## Credit
-
-Code extracted from [MailCheck](https://github.com/kicksend/mailcheck)
-
-## License
-
- MIT
-
-================================================================================
-
-stringify-object@3.3.0
-
-Copyright (c) 2015, Yeoman team
-All rights reserved.
+MIT License
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================================================
-tmp-cache@1.1.0
+vscode-uri@3.0.2
The MIT License (MIT)
-Copyright (c) Luke Edwards (lukeed.com)
+Copyright (c) Microsoft
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
From 829b1bf680453b813c173953c8dbc5230e18be72 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 25 Feb 2025 11:14:11 -0500
Subject: [PATCH 002/108] 0.14.7
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index f8736d19..93b2ea93 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.6",
+ "version": "0.14.7",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index d9b2011d..39bea0b3 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.6",
+ "version": "0.14.7",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index be197250..9889da1e 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.7
+
- LSP: Declare capability for handling workspace folder change notifications ([#1223](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1223))
- Don't throw when resolving paths containing a `#` character ([#1225](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1225))
- Show `@theme` in symbol list in CSS language mode ([#1227](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1227))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 02cd21b7..cdeadb3c 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.6",
+ "version": "0.14.7",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 98b64b72a9d2cf0f321b47887162d1dcc32d66d3 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 25 Feb 2025 11:22:52 -0500
Subject: [PATCH 003/108] Fix tests on earlier Node versions
---
packages/tailwindcss-language-server/tests/utils/client.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/utils/client.ts b/packages/tailwindcss-language-server/tests/utils/client.ts
index e02a8393..f7ee6e94 100644
--- a/packages/tailwindcss-language-server/tests/utils/client.ts
+++ b/packages/tailwindcss-language-server/tests/utils/client.ts
@@ -45,7 +45,6 @@ import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/
import { DefaultMap } from '../../src/util/default-map'
import { connect, ConnectOptions } from './connection'
import type { DeepPartial } from './types'
-import { styleText } from 'node:util'
export interface DocumentDescriptor {
/**
@@ -237,7 +236,8 @@ export interface ClientWorkspace {
function trace(msg: string, ...args: any[]) {
console.log(
- `${styleText(['bold', 'blue', 'inverse'], ' TEST ')} ${styleText('dim', msg)}`,
+ // `${styleText(['bold', 'blue', 'inverse'], ' TEST ')} ${styleText('dim', msg)}`,
+ ` TEST ${msg}`,
...args,
)
}
From 83010327316223eb596fae66e4909a354e7e54b4 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 25 Feb 2025 11:28:47 -0500
Subject: [PATCH 004/108] Disable some concurrent tests
The tests are now running _too_ fast and are revealing race conditions in the server
---
.../tailwindcss-language-server/tests/hover/hover.test.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/hover/hover.test.js b/packages/tailwindcss-language-server/tests/hover/hover.test.js
index 63c2e32c..5c8bcb7f 100644
--- a/packages/tailwindcss-language-server/tests/hover/hover.test.js
+++ b/packages/tailwindcss-language-server/tests/hover/hover.test.js
@@ -6,7 +6,7 @@ withFixture('basic', (c) => {
name,
{ text, lang, position, exact = false, expected, expectedRange, settings },
) {
- test.concurrent(name, async ({ expect }) => {
+ test(name, async ({ expect }) => {
let textDocument = await c.openDocument({ text, lang, settings })
let res = await c.sendRequest('textDocument/hover', {
textDocument,
@@ -180,7 +180,7 @@ withFixture('v4/basic', (c) => {
name,
{ text, exact = false, lang, position, expected, expectedRange, settings },
) {
- test.concurrent(name, async ({ expect }) => {
+ test(name, async ({ expect }) => {
let textDocument = await c.openDocument({ text, lang, settings })
let res = await c.sendRequest('textDocument/hover', {
textDocument,
@@ -314,7 +314,7 @@ withFixture('v4/basic', (c) => {
withFixture('v4/css-loading-js', (c) => {
async function testHover(name, { text, lang, position, expected, expectedRange, settings }) {
- test.concurrent(name, async ({ expect }) => {
+ test(name, async ({ expect }) => {
let textDocument = await c.openDocument({ text, lang, settings })
let res = await c.sendRequest('textDocument/hover', {
textDocument,
@@ -398,7 +398,7 @@ withFixture('v4/css-loading-js', (c) => {
withFixture('v4/path-mappings', (c) => {
async function testHover(name, { text, lang, position, expected, expectedRange, settings }) {
- test.concurrent(name, async ({ expect }) => {
+ test(name, async ({ expect }) => {
let textDocument = await c.openDocument({ text, lang, settings })
let res = await c.sendRequest('textDocument/hover', {
textDocument,
From eabd07e405ccdd1c9bf3e074ffb45ba917068f50 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 26 Feb 2025 12:52:07 -0500
Subject: [PATCH 005/108] Fix issue when require()-ing packages that resolve to
paths containing `#` (#1235)
Fixes #1221 (again, for real this time, I hope)
---
.../src/testing/index.ts | 20 ++++-
.../src/util/resolveFrom.ts | 12 +++
.../tests/env/v4.test.js | 88 ++++++++++++++++++-
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
4 files changed, 119 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts
index 2435ca0f..21296fc7 100644
--- a/packages/tailwindcss-language-server/src/testing/index.ts
+++ b/packages/tailwindcss-language-server/src/testing/index.ts
@@ -11,12 +11,13 @@ export interface TestUtils {
export interface Storage {
/** A list of files and their content */
- [filePath: string]: string | Uint8Array
+ [filePath: string]: string | Uint8Array | { [IS_A_SYMLINK]: true; filepath: string }
}
export interface TestConfig {
name: string
fs?: Storage
+ debug?: boolean
prepare?(utils: TestUtils): Promise
handle(utils: TestUtils & Extras): void | Promise
@@ -56,6 +57,8 @@ async function setup(config: TestConfig): Promise {
if (path.sep === '\\') return
+ if (config.debug) return
+
// Remove the directory on *nix systems. Recursive removal on Windows will
// randomly fail b/c its slow and buggy.
await fs.rm(doneDir, { recursive: true })
@@ -66,6 +69,14 @@ async function setup(config: TestConfig): Promise {
}
}
+const IS_A_SYMLINK = Symbol('is-a-symlink')
+export const symlinkTo = function (filepath: string) {
+ return {
+ [IS_A_SYMLINK]: true as const,
+ filepath,
+ }
+}
+
async function prepareFileSystem(base: string, storage: Storage) {
// Create a temporary directory to store the test files
await fs.mkdir(base, { recursive: true })
@@ -74,6 +85,13 @@ async function prepareFileSystem(base: string, storage: Storage) {
for (let [filepath, content] of Object.entries(storage)) {
let fullPath = path.resolve(base, filepath)
await fs.mkdir(path.dirname(fullPath), { recursive: true })
+
+ if (typeof content === 'object' && IS_A_SYMLINK in content) {
+ let target = path.resolve(base, content.filepath)
+ await fs.symlink(target, fullPath)
+ continue
+ }
+
await fs.writeFile(fullPath, content, { encoding: 'utf-8' })
}
}
diff --git a/packages/tailwindcss-language-server/src/util/resolveFrom.ts b/packages/tailwindcss-language-server/src/util/resolveFrom.ts
index 2b62bbeb..c4ef1a15 100644
--- a/packages/tailwindcss-language-server/src/util/resolveFrom.ts
+++ b/packages/tailwindcss-language-server/src/util/resolveFrom.ts
@@ -56,5 +56,17 @@ export function resolveFrom(from?: string, id?: string): string {
let result = resolver.resolveSync({}, from, id)
if (result === false) throw Error()
+
+ // The `enhanced-resolve` package supports resolving paths with fragment
+ // identifiers. For example, it can resolve `foo/bar#baz` to `foo/bar.js`
+ // However, it's also possible that a path contains a `#` character as part
+ // of the path itself. For example, `foo#bar` might point to a file named
+ // `foo#bar.js`. The resolver distinguishes between these two cases by
+ // escaping the `#` character with a NUL byte when it's part of the path.
+ //
+ // Since the real path doesn't actually contain NUL bytes, we need to remove
+ // them to get the correct path otherwise readFileSync will throw an error.
+ result = result.replace(/\0(.)/g, '$1')
+
return result
}
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 1ae5caf4..6e5aa34c 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -1,7 +1,7 @@
// @ts-check
import { expect } from 'vitest'
-import { css, defineTest, html, js, json } from '../../src/testing'
+import { css, defineTest, html, js, json, symlinkTo } from '../../src/testing'
import dedent from 'dedent'
import { createClient } from '../utils/client'
@@ -666,3 +666,89 @@ defineTest({
})
},
})
+
+defineTest({
+ // This test *always* passes inside Vitest because our custom version of
+ // `Module._resolveFilename` is not called. Our custom implementation is
+ // using enhanced-resolve under the hood which is affected by the `#`
+ // character issue being considered a fragment identifier.
+ //
+ // This most commonly happens when dealing with PNPM packages that point
+ // to a specific commit hash of a git repository.
+ //
+ // To simulate this, we need to:
+ // - Add a local package to package.json
+ // - Symlink that local package to a directory with `#` in the name
+ // - Then run the test in a separate process (`spawn` mode)
+ //
+ // We can't use `file:./a#b` because NPM considers `#` to be a fragment
+ // identifier and will not resolve the path the way we need it to.
+ name: 'v3: require() works when path is resolved to contain a `#`',
+ fs: {
+ 'package.json': json`
+ {
+ "dependencies": {
+ "tailwindcss": "3.4.17",
+ "some-pkg": "file:./packages/some-pkg"
+ }
+ }
+ `,
+ 'tailwind.config.js': js`
+ module.exports = {
+ presets: [require('some-pkg/config/tailwind.config.js').default]
+ }
+ `,
+ 'packages/some-pkg': symlinkTo('packages/some-pkg#c3f1e'),
+ 'packages/some-pkg#c3f1e/package.json': json`
+ {
+ "name": "some-pkg",
+ "version": "1.0.0",
+ "main": "index.js"
+ }
+ `,
+ 'packages/some-pkg#c3f1e/config/tailwind.config.js': js`
+ export default {
+ plugins: [
+ function ({ addUtilities }) {
+ addUtilities({
+ '.example': {
+ color: 'red',
+ },
+ })
+ }
+ ]
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ mode: 'spawn',
+ }),
+ }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ //
+ // ^
+ let hover = await document.hover({ line: 0, character: 13 })
+
+ expect(hover).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .example {
+ color: red;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 19 },
+ },
+ })
+ },
+})
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 9889da1e..9da256e0 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Don't throw when requiring() packages that resolve to a path containing a `#` character ([#1235](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1235))
# 0.14.7
From 4cb548e60519036a1328d989ab1ded266bb4abf9 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 26 Feb 2025 12:52:55 -0500
Subject: [PATCH 006/108] Fix syntax error when resetting multi-word theme key
namespaces (#1237)
Fixes #1236
This PR fixes an issue where we'd show a syntax error for multi-word
namespace resets like `--font-weight-*: initial;`:
```css
@theme {
--font-*: initial; /* this one is fine */
--font-weight-*: initial; /* this one shows a syntax error */
}
```
Now both work as expected:
---
.../src/language/rewriting.test.ts | 16 ++++++++++++----
.../src/language/rewriting.ts | 2 +-
.../tests/css/css-server.test.ts | 3 ++-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
4 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/language/rewriting.test.ts b/packages/tailwindcss-language-server/src/language/rewriting.test.ts
index 33596a2e..d3874418 100644
--- a/packages/tailwindcss-language-server/src/language/rewriting.test.ts
+++ b/packages/tailwindcss-language-server/src/language/rewriting.test.ts
@@ -69,20 +69,28 @@ test('@theme', () => {
let input = [
//
'@theme {',
- ' color: red;',
+ ' --color: red;',
+ ' --font-*: initial;',
+ ' --font-weight-*: initial;',
'}',
'@theme inline reference static default {',
- ' color: red;',
+ ' --color: red;',
+ ' --font-*: initial;',
+ ' --font-weight-*: initial;',
'}',
]
let output = [
//
'.placeholder {', // wrong
- ' color: red;',
+ ' --color: red;',
+ ' --font-_: initial;',
+ ' --font-weight-_: initial;',
'}',
'.placeholder {', // wrong
- ' color: red;',
+ ' --color: red;',
+ ' --font-_: initial;',
+ ' --font-weight-_: initial;',
'}',
]
diff --git a/packages/tailwindcss-language-server/src/language/rewriting.ts b/packages/tailwindcss-language-server/src/language/rewriting.ts
index 4dc7a429..4a983ad1 100644
--- a/packages/tailwindcss-language-server/src/language/rewriting.ts
+++ b/packages/tailwindcss-language-server/src/language/rewriting.ts
@@ -74,7 +74,7 @@ export function rewriteCss(css: string) {
})
// Replace `--some-var-*` with `--some-var-_`
- css = css.replace(/--([a-zA-Z0-9]+)-[*]/g, '--$1_')
+ css = css.replace(/--([a-zA-Z0-9-]+)-[*]/g, '--$1-_')
return css
}
diff --git a/packages/tailwindcss-language-server/tests/css/css-server.test.ts b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
index 02146ccc..1ee3246b 100644
--- a/packages/tailwindcss-language-server/tests/css/css-server.test.ts
+++ b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
@@ -219,6 +219,7 @@ defineTest({
@theme {
--color-primary: #333;
--leading-*: initial;
+ --font-weight-*: initial;
}
`,
})
@@ -235,7 +236,7 @@ defineTest({
uri: '{workspace:default}/file-1.css',
range: {
start: { line: 1, character: 0 },
- end: { line: 4, character: 1 },
+ end: { line: 5, character: 1 },
},
},
},
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 9da256e0..2f2ce33c 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- Don't throw when requiring() packages that resolve to a path containing a `#` character ([#1235](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1235))
+- Fix syntax error when resetting multi-word theme key namespaces ([#1237](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1237))
# 0.14.7
From 4d9b7366301bb296467a75d32d00355744b477c7 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 26 Feb 2025 13:08:00 -0500
Subject: [PATCH 007/108] 0.14.8
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 93b2ea93..16d4f73b 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.7",
+ "version": "0.14.8",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 39bea0b3..0cbdde25 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.7",
+ "version": "0.14.8",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 2f2ce33c..18b39e02 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.8
+
- Don't throw when requiring() packages that resolve to a path containing a `#` character ([#1235](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1235))
- Fix syntax error when resetting multi-word theme key namespaces ([#1237](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1237))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index cdeadb3c..08906fbd 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.7",
+ "version": "0.14.8",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 34660f8026baa79d2fe10e1ee7db826be66e1139 Mon Sep 17 00:00:00 2001
From: Philipp Spiess
Date: Thu, 27 Feb 2025 14:59:54 +0100
Subject: [PATCH 008/108] Bring back issue template
---
.github/ISSUE_TEMPLATE/bug-report.md | 47 ++++++++++++++++++++++++++++
.github/ISSUE_TEMPLATE/config.yml | 3 --
2 files changed, 47 insertions(+), 3 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 00000000..3809535c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -0,0 +1,47 @@
+---
+name: Bug report
+about: If you think something is broken with Tailwind CSS IntelliSense itself, create a bug report.
+title: ''
+labels: ''
+assignees: ''
+---
+
+**What version of VS Code are you using?**
+
+For example: v1.78.2
+
+**What version of Tailwind CSS IntelliSense are you using?**
+
+For example: v0.7.0
+
+**What version of Tailwind CSS are you using?**
+
+For example: v2.0.4
+
+**What package manager are you using?**
+
+For example: npm, yarn
+
+**What operating system are you using?**
+
+For example: macOS, Windows
+
+**Tailwind config**
+
+```js
+// Paste the contents of your Tailwind config file here
+```
+
+**VS Code settings**
+
+```json
+// Paste your VS Code settings in JSON format here
+```
+
+**Reproduction URL**
+
+A public GitHub repo that includes a minimal reproduction of the bug. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn't matter if your real project is private/confidential, since we want a link to a separate, isolated reproduction anyways.
+
+**Describe your issue**
+
+Describe the problem you're seeing, any important steps to reproduce and what behavior you expect instead
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 1f144920..7c5bf993 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,6 +3,3 @@ contact_links:
- name: Feature Request
url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas&title=%5BIntelliSense%5D%20
about: 'Suggest any ideas you have using our discussion forums.'
- - name: Bug Report
- url: https://github.com/tailwindlabs/tailwindcss-intellisense/issues/new?body=**What%20version%20of%20VS%20Code%20are%20you%20using%3F**%0A%0AFor%20example%3A%20v1.78.2%0A%0A**What%20version%20of%20Tailwind%20CSS%20IntelliSense%20are%20you%20using%3F**%0A%0AFor%20example%3A%20v0.7.0%0A%0A**What%20version%20of%20Tailwind%20CSS%20are%20you%20using%3F**%0A%0AFor%20example%3A%20v2.0.4%0A%0A**What%20package%20manager%20are%20you%20using%3F**%0A%0AFor%20example%3A%20npm%2C%20yarn%0A%0A**What%20operating%20system%20are%20you%20using%3F**%0A%0AFor%20example%3A%20macOS%2C%20Windows%0A%0A**Tailwind%20config**%0A%0A%60%60%60js%0A%2F%2F%20Paste%20the%20contents%20of%20your%20Tailwind%20config%20file%20here%0A%60%60%60%0A%0A**VS%20Code%20settings**%0A%0A%60%60%60json%0A%2F%2F%20Paste%20your%20VS%20Code%20settings%20in%20JSON%20format%20here%0A%60%60%60%0A%0A**Reproduction%20URL**%0A%0AA%20public%20GitHub%20repo%20that%20includes%20a%20minimal%20reproduction%20of%20the%20bug.%20**Please%20do%20not%20link%20to%20your%20actual%20project**%2C%20what%20we%20need%20instead%20is%20a%20_minimal_%20reproduction%20in%20a%20fresh%20project%20without%20any%20unnecessary%20code.%20This%20means%20it%20doesn%27t%20matter%20if%20your%20real%20project%20is%20private%2Fconfidential%2C%20since%20we%20want%20a%20link%20to%20a%20separate%2C%20isolated%20reproduction%20anyways.%0A%0A**Describe%20your%20issue**%0A%0ADescribe%20the%20problem%20you%27re%20seeing%2C%20any%20important%20steps%20to%20reproduce%20and%20what%20behavior%20you%20expect%20instead.
- about: If you think something is broken with Tailwind CSS IntelliSense itself, create a bug report.
From c8efecac2dd342ff8053b49aa0d0648cbc6bb370 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Feb 2025 11:44:29 -0500
Subject: [PATCH 009/108] v4: Support loading bundled versions of some
first-party plugins (#1240)
see
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1224#issuecomment-2687637180
This adds support for loading these three plugins in v4 **only when
using `@plugin`** if we can't find them:
- `@tailwindcss/typography`
- `@tailwindcss/forms`
- `@tailwindcss/aspect-ratio`
This coincides with behavior of the Standalone CLI where these are
bundled instead of available as an NPM package.
I've additionally logs when trying to import one of these plugins inside
a JS file. This does not work right now and may take some effort to
support because it will at least require the use of Node's experimental
loader's API and potentially additional work as well.
---
.../src/util/v4/design-system.ts | 23 +++++++++++
.../src/util/v4/plugins.ts | 5 +++
.../tests/env/v4.test.js | 39 +++++++++++++++++++
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
4 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 packages/tailwindcss-language-server/src/util/v4/plugins.ts
diff --git a/packages/tailwindcss-language-server/src/util/v4/design-system.ts b/packages/tailwindcss-language-server/src/util/v4/design-system.ts
index f32305ce..05d2ecd3 100644
--- a/packages/tailwindcss-language-server/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-server/src/util/v4/design-system.ts
@@ -9,6 +9,7 @@ import { Resolver } from '../../resolver'
import { pathToFileURL } from '../../utils'
import type { Jiti } from 'jiti/lib/types'
import { assets } from './assets'
+import { plugins } from './plugins'
const HAS_V4_IMPORT = /@import\s*(?:'tailwindcss'|"tailwindcss")/
const HAS_V4_THEME = /@theme\s*\{/
@@ -58,6 +59,28 @@ function createLoader({
return await jiti.import(url.href, { default: true })
} catch (err) {
+ // If the request was to load a first-party plugin and we can't resolve it
+ // locally, then fall back to the built-in plugins that we know about.
+ if (resourceType === 'plugin' && id in plugins) {
+ console.log('Loading bundled plugin for: ', id)
+ return await plugins[id]()
+ }
+
+ // This checks for an error thrown by enhanced-resolve
+ if (err && typeof err.details === 'string') {
+ let details: string = err.details
+ let pattern = /^resolve '([^']+)'/
+ let match = details.match(pattern)
+ if (match) {
+ let [_, importee] = match
+ if (importee in plugins) {
+ console.log(
+ `[error] Cannot load '${id}' plugins inside configs or plugins is not currently supported`,
+ )
+ }
+ }
+ }
+
return onError(id, err, resourceType)
}
}
diff --git a/packages/tailwindcss-language-server/src/util/v4/plugins.ts b/packages/tailwindcss-language-server/src/util/v4/plugins.ts
new file mode 100644
index 00000000..16efc4a5
--- /dev/null
+++ b/packages/tailwindcss-language-server/src/util/v4/plugins.ts
@@ -0,0 +1,5 @@
+export const plugins = {
+ '@tailwindcss/forms': () => import('@tailwindcss/forms').then((m) => m.default),
+ '@tailwindcss/aspect-ratio': () => import('@tailwindcss/aspect-ratio').then((m) => m.default),
+ '@tailwindcss/typography': () => import('@tailwindcss/typography').then((m) => m.default),
+}
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 6e5aa34c..76de7f15 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -53,6 +53,45 @@ defineTest({
},
})
+defineTest({
+ name: 'v4, no npm, bundled plugins',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ @plugin "@tailwindcss/aspect-ratio";
+ @plugin "@tailwindcss/forms";
+ @plugin "@tailwindcss/typography";
+ `,
+ },
+
+ // Note this test MUST run in spawn mode because Vitest hooks into import,
+ // require, etc… already and we need to test that any hooks are working
+ // without outside interference.
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let hover = await doc.hover({ line: 0, character: 13 })
+ expect(hover).not.toEqual(null)
+
+ //
+ // ^
+ hover = await doc.hover({ line: 0, character: 25 })
+ expect(hover).not.toEqual(null)
+
+ //
+ // ^
+ hover = await doc.hover({ line: 0, character: 37 })
+ expect(hover).not.toEqual(null)
+ },
+})
+
defineTest({
/**
* Plugins and configs that import stuff from the `tailwindcss` package do
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 18b39e02..d22e380c 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- v4: Support loading bundled versions of some first-party plugins ([#1240](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1240))
# 0.14.8
From 23a03cbfda51a8711da24a92a5d8b4a30ad53a1d Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 11 Mar 2025 15:19:16 -0400
Subject: [PATCH 010/108] Cancel initial file search if it takes too long
(#1242)
Fixes #986 (hopefully)
There's a lot of detail about the problem in the linked issue and my
response at the bottom. But briefly:
- Searches can take way too long; even spinning "forever" for some
people
- This occurs more often if following symlinks is enabled in VSCode
- We can cancel in-flight searches
This PR starts the searches and cancels them after ~15s. If the
extension hasn't found anything within 15s then it's likely it'll take
~15s any time you open that project and that would've been an absolutely
horrible dev experience that we probably would've been notified about.
It seems reasonable to set a time limit on the search do determine if we
need to start a language server
---
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
packages/vscode-tailwindcss/src/extension.ts | 35 +++++++++++++++++---
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index d22e380c..01bf3b4a 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- v4: Support loading bundled versions of some first-party plugins ([#1240](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1240))
+- Cancel initial file search if it takes too long ([#1242](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1242))
# 0.14.8
diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts
index c0c7b9e7..a3748616 100755
--- a/packages/vscode-tailwindcss/src/extension.ts
+++ b/packages/vscode-tailwindcss/src/extension.ts
@@ -6,6 +6,7 @@ import type {
ConfigurationScope,
WorkspaceConfiguration,
Selection,
+ CancellationToken,
} from 'vscode'
import {
workspace as Workspace,
@@ -16,6 +17,7 @@ import {
Position,
Range,
RelativePattern,
+ CancellationTokenSource,
} from 'vscode'
import type {
DocumentFilter,
@@ -581,18 +583,34 @@ export async function activate(context: ExtensionContext) {
return
}
- if (!(await anyFolderNeedsLanguageServer(Workspace.workspaceFolders ?? []))) {
+ let source: CancellationTokenSource | null = new CancellationTokenSource()
+ source.token.onCancellationRequested(() => {
+ source?.dispose()
+ source = null
+ outputChannel.appendLine(
+ 'Server was not started. Search for Tailwind CSS-related files was taking too long.',
+ )
+ })
+
+ // Cancel the search after roughly 15 seconds
+ setTimeout(() => source?.cancel(), 15_000)
+
+ if (!(await anyFolderNeedsLanguageServer(Workspace.workspaceFolders ?? [], source!.token))) {
+ source?.dispose()
return
}
+ source?.dispose()
+
await bootWorkspaceClient()
}
async function anyFolderNeedsLanguageServer(
folders: readonly WorkspaceFolder[],
+ token: CancellationToken,
): Promise {
for (let folder of folders) {
- if (await folderNeedsLanguageServer(folder)) {
+ if (await folderNeedsLanguageServer(folder, token)) {
return true
}
}
@@ -600,7 +618,10 @@ export async function activate(context: ExtensionContext) {
return false
}
- async function folderNeedsLanguageServer(folder: WorkspaceFolder): Promise {
+ async function folderNeedsLanguageServer(
+ folder: WorkspaceFolder,
+ token: CancellationToken,
+ ): Promise {
let settings = Workspace.getConfiguration('tailwindCSS', folder)
if (settings.get('experimental.configFile') !== null) {
return true
@@ -616,13 +637,19 @@ export async function activate(context: ExtensionContext) {
new RelativePattern(folder, `**/${CONFIG_GLOB}`),
exclude,
1,
+ token,
)
for (let file of configFiles) {
return true
}
- let cssFiles = await Workspace.findFiles(new RelativePattern(folder, `**/${CSS_GLOB}`), exclude)
+ let cssFiles = await Workspace.findFiles(
+ new RelativePattern(folder, `**/${CSS_GLOB}`),
+ exclude,
+ undefined,
+ token,
+ )
for (let file of cssFiles) {
outputChannel.appendLine(`Checking if ${file.fsPath} may be Tailwind-related…`)
From 07da721de845f5262a536c1451f47dcc705ccd71 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 12 Mar 2025 09:30:57 -0400
Subject: [PATCH 011/108] =?UTF-8?q?Don=E2=80=99t=20throw=20when=20client?=
=?UTF-8?q?=20is=20missing=20the=20`textDocument`=20capability=20(#1252)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes #1251
---
packages/tailwindcss-language-server/src/tw.ts | 10 +++++-----
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index efb12a34..35da9386 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -1024,10 +1024,10 @@ export class TW {
function supportsDynamicRegistration(params: InitializeParams): boolean {
return (
- params.capabilities.textDocument.hover?.dynamicRegistration &&
- params.capabilities.textDocument.colorProvider?.dynamicRegistration &&
- params.capabilities.textDocument.codeAction?.dynamicRegistration &&
- params.capabilities.textDocument.completion?.dynamicRegistration &&
- params.capabilities.textDocument.documentLink?.dynamicRegistration
+ params.capabilities.textDocument?.hover?.dynamicRegistration &&
+ params.capabilities.textDocument?.colorProvider?.dynamicRegistration &&
+ params.capabilities.textDocument?.codeAction?.dynamicRegistration &&
+ params.capabilities.textDocument?.completion?.dynamicRegistration &&
+ params.capabilities.textDocument?.documentLink?.dynamicRegistration
)
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 01bf3b4a..cd3f2170 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,7 @@
- v4: Support loading bundled versions of some first-party plugins ([#1240](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1240))
- Cancel initial file search if it takes too long ([#1242](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1242))
+- LSP: Don’t throw when the client does not provide `textDocument` in capabilities ([#1252](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1252))
# 0.14.8
From 0cddb23899846ab485506dbb77f86f478b757642 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 12 Mar 2025 09:33:02 -0400
Subject: [PATCH 012/108] Rewrite all occurrences of `*` in a CSS variable name
(#1256)
Fixes #1248
---
.../src/language/css-server.ts | 2 +-
.../src/language/rewriting.test.ts | 28 ++++++++++++++-----
.../src/language/rewriting.ts | 26 ++++++++++-------
.../tests/css/css-server.test.ts | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
5 files changed, 40 insertions(+), 19 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/language/css-server.ts b/packages/tailwindcss-language-server/src/language/css-server.ts
index a146cea4..a43910b2 100644
--- a/packages/tailwindcss-language-server/src/language/css-server.ts
+++ b/packages/tailwindcss-language-server/src/language/css-server.ts
@@ -225,7 +225,7 @@ export class CssServer {
if (match) {
symbol.name = `${match[1]} ${match[2]?.trim() ?? match[3]?.trim()}`
}
- } else if (symbol.name === `.placeholder`) {
+ } else if (/^\._+$/.test(symbol.name)) {
let doc = documents.get(symbol.location.uri)
let text = doc.getText(symbol.location.range)
let match = text.trim().match(/^(@[^\s]+)(?:([^{]+)[{]|([^;{]+);)/)
diff --git a/packages/tailwindcss-language-server/src/language/rewriting.test.ts b/packages/tailwindcss-language-server/src/language/rewriting.test.ts
index d3874418..23249932 100644
--- a/packages/tailwindcss-language-server/src/language/rewriting.test.ts
+++ b/packages/tailwindcss-language-server/src/language/rewriting.test.ts
@@ -50,16 +50,22 @@ test('@utility', () => {
'@utility foo-* {',
' color: red;',
'}',
+ '@utility bar-* {',
+ ' color: --value(--font-*-line-height);',
+ '}',
]
let output = [
//
- '.placeholder {', // wrong
+ '._______ {',
' color: red;',
'}',
- '.placeholder {', // wrong
+ '._______ {',
' color: red;',
'}',
+ '._______ {',
+ ' color: --value(--font-_-line-height);',
+ '}',
]
expect(rewriteCss(input.join('\n'))).toBe(output.join('\n'))
@@ -70,11 +76,15 @@ test('@theme', () => {
//
'@theme {',
' --color: red;',
+ ' --*: initial;',
+ ' --text*: initial;',
' --font-*: initial;',
' --font-weight-*: initial;',
'}',
'@theme inline reference static default {',
' --color: red;',
+ ' --*: initial;',
+ ' --text*: initial;',
' --font-*: initial;',
' --font-weight-*: initial;',
'}',
@@ -82,13 +92,17 @@ test('@theme', () => {
let output = [
//
- '.placeholder {', // wrong
+ '._____ {',
' --color: red;',
+ ' --_: initial;',
+ ' --text_: initial;',
' --font-_: initial;',
' --font-weight-_: initial;',
'}',
- '.placeholder {', // wrong
+ '._____ {',
' --color: red;',
+ ' --_: initial;',
+ ' --text_: initial;',
' --font-_: initial;',
' --font-weight-_: initial;',
'}',
@@ -110,8 +124,8 @@ test('@custom-variant', () => {
let output = [
//
- '@media (℘) {}', // wrong
- '.placeholder {', // wrong
+ '@media(℘) {}',
+ '.______________ {',
' &:hover {',
' @slot;',
' }',
@@ -133,7 +147,7 @@ test('@variant', () => {
let output = [
//
- '.placeholder {', // wrong
+ '._______ {',
' &:hover {',
' @slot;',
' }',
diff --git a/packages/tailwindcss-language-server/src/language/rewriting.ts b/packages/tailwindcss-language-server/src/language/rewriting.ts
index 4a983ad1..1d660709 100644
--- a/packages/tailwindcss-language-server/src/language/rewriting.ts
+++ b/packages/tailwindcss-language-server/src/language/rewriting.ts
@@ -14,9 +14,10 @@ function replaceWithAtRule(delta = 0) {
}
function replaceWithStyleRule(delta = 0) {
- return (_match: string, p1: string) => {
+ return (_match: string, name: string, p1: string) => {
+ let className = '_'.repeat(name.length)
let spaces = ' '.repeat(p1.length + delta)
- return `.placeholder${spaces}{`
+ return `.${className}${spaces}{`
}
}
@@ -36,16 +37,16 @@ export function rewriteCss(css: string) {
css = css.replace(/@screen(\s+[^{]+){/g, replaceWithAtRule(-2))
css = css.replace(/@variants(\s+[^{]+){/g, replaceWithAtRule())
css = css.replace(/@responsive(\s*){/g, replaceWithAtRule())
- css = css.replace(/@utility(\s+[^{]+){/g, replaceWithStyleRule())
- css = css.replace(/@theme(\s+[^{]*){/g, replaceWithStyleRule())
+ css = css.replace(/@(utility)(\s+[^{]+){/g, replaceWithStyleRule())
+ css = css.replace(/@(theme)(\s+[^{]*){/g, replaceWithStyleRule())
- css = css.replace(/@custom-variant(\s+[^;{]+);/g, (match: string) => {
- let spaces = ' '.repeat(match.length - 11)
- return `@media (${MEDIA_MARKER})${spaces}{}`
+ css = css.replace(/@(custom-variant)(\s+[^;{]+);/g, (match: string, name: string) => {
+ let spaces = ' '.repeat(match.length - name.length + 3)
+ return `@media(${MEDIA_MARKER})${spaces}{}`
})
- css = css.replace(/@custom-variant(\s+[^{]+){/g, replaceWithStyleRule())
- css = css.replace(/@variant(\s+[^{]+){/g, replaceWithStyleRule())
+ css = css.replace(/@(custom-variant)(\s+[^{]+){/g, replaceWithStyleRule())
+ css = css.replace(/@(variant)(\s+[^{]+){/g, replaceWithStyleRule())
css = css.replace(/@layer(\s+[^{]{2,}){/g, replaceWithAtRule(-3))
css = css.replace(/@reference\s*([^;]{2,})/g, '@import $1')
@@ -73,8 +74,13 @@ export function rewriteCss(css: string) {
return match.replace(/[*]/g, '_')
})
+ // Replace `--*` with `--_`
+ // Replace `--some-var*` with `--some-var_`
// Replace `--some-var-*` with `--some-var-_`
- css = css.replace(/--([a-zA-Z0-9-]+)-[*]/g, '--$1-_')
+ // Replace `--text-*-line-height` with `--text-_-line-height`
+ css = css.replace(/--([a-zA-Z0-9-*]*)/g, (match) => {
+ return match.replace(/[*]/g, '_')
+ })
return css
}
diff --git a/packages/tailwindcss-language-server/tests/css/css-server.test.ts b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
index 1ee3246b..e8970e08 100644
--- a/packages/tailwindcss-language-server/tests/css/css-server.test.ts
+++ b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
@@ -38,7 +38,7 @@ defineTest({
uri: '{workspace:default}/file-1.css',
range: {
start: { line: 1, character: 0 },
- end: { line: 1, character: 31 },
+ end: { line: 1, character: 30 },
},
},
},
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index cd3f2170..f5982607 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -5,6 +5,7 @@
- v4: Support loading bundled versions of some first-party plugins ([#1240](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1240))
- Cancel initial file search if it takes too long ([#1242](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1242))
- LSP: Don’t throw when the client does not provide `textDocument` in capabilities ([#1252](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1252))
+- v4: Allow `*` anywhere in a CSS variable name ([#1256](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1256))
# 0.14.8
From 7a4f8d5b64bd79f7be5a10de46817a2e48f36206 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 12 Mar 2025 10:46:58 -0400
Subject: [PATCH 013/108] 0.14.9
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 16d4f73b..5ed431fb 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.8",
+ "version": "0.14.9",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 0cbdde25..f1047879 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.8",
+ "version": "0.14.9",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index f5982607..ca4255d7 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.9
+
- v4: Support loading bundled versions of some first-party plugins ([#1240](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1240))
- Cancel initial file search if it takes too long ([#1242](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1242))
- LSP: Don’t throw when the client does not provide `textDocument` in capabilities ([#1252](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1252))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 08906fbd..3b5fd76a 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.8",
+ "version": "0.14.9",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From b4b3a2a0351a2135e1bd34df7a22aae53288e540 Mon Sep 17 00:00:00 2001
From: Laurynas Grigutis <34269850+LaurynasGr@users.noreply.github.com>
Date: Wed, 19 Mar 2025 15:54:20 +0200
Subject: [PATCH 014/108] Add `tailwindCSS.classFunctions` setting (#1258)
This PR adds `tailwindCSS.classFunctions` option to the settings to add
simple and performant class completions, hover previews, linting etc.
for such cases:
```ts
const classes = cn(
'pointer-events-auto relative flex bg-red-500',
'items-center justify-between overflow-hidden',
'md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full',
'data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)]',
Date.now() > 15000 ? 'text-red-200' : 'text-red-700',
'data-[swipe=move]:transition-none',
)
```

```ts
const variants = cva(
cn(
'pointer-events-auto relative flex bg-green-500',
'md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full',
'md:h-[calc(100%-2rem)]',
'bg-red-700',
),
{
variants: {
mobile: {
default: 'bottom-0 left-0',
fullScreen: `
inset-0
md:h-[calc(100%-2rem)]
rounded-none
bg-blue-700
`,
},
},
defaultVariants: {
mobile: 'default',
},
},
)
```

```ts
const tagged = cn`
pointer-events-auto relative flex bg-red-500
items-center justify-between overflow-hidden
md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full
data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)]
md:h-[calc(100%-2rem)] text-green-700
data-[swipe=move]:transition-none
`
```

---------
Co-authored-by: Laurynas Grigutis
Co-authored-by: Jordan Pittman
---
.../tailwindcss-language-server/src/config.ts | 41 +-
.../tests/utils/client.ts | 2 +-
.../tests/utils/configuration.ts | 41 +-
.../tests/utils/types.ts | 9 -
.../tailwindcss-language-service/package.json | 2 +
.../scripts/build.mjs | 2 +-
.../scripts/tsconfig.build.json | 4 +
.../src/completionProvider.ts | 23 +-
.../tailwindcss-language-service/src/types.ts | 9 +
.../src/util/find.test.ts | 800 ++++++++++++++++--
.../src/util/find.ts | 143 +++-
.../src/util/lexers.ts | 9 +
.../src/util/resolveRange.ts | 16 -
.../src/util/state.ts | 73 +-
.../tsconfig.json | 2 +-
packages/vscode-tailwindcss/README.md | 20 +
packages/vscode-tailwindcss/package.json | 8 +
pnpm-lock.yaml | 11 +
18 files changed, 999 insertions(+), 216 deletions(-)
delete mode 100644 packages/tailwindcss-language-server/tests/utils/types.ts
create mode 100644 packages/tailwindcss-language-service/scripts/tsconfig.build.json
create mode 100644 packages/tailwindcss-language-service/src/types.ts
delete mode 100644 packages/tailwindcss-language-service/src/util/resolveRange.ts
diff --git a/packages/tailwindcss-language-server/src/config.ts b/packages/tailwindcss-language-server/src/config.ts
index d8364d06..a5e68f7d 100644
--- a/packages/tailwindcss-language-server/src/config.ts
+++ b/packages/tailwindcss-language-server/src/config.ts
@@ -1,6 +1,9 @@
import merge from 'deepmerge'
import { isObject } from './utils'
-import type { Settings } from '@tailwindcss/language-service/src/util/state'
+import {
+ getDefaultTailwindSettings,
+ type Settings,
+} from '@tailwindcss/language-service/src/util/state'
import type { Connection } from 'vscode-languageserver'
export interface SettingsCache {
@@ -8,40 +11,6 @@ export interface SettingsCache {
clear(): void
}
-function getDefaultSettings(): Settings {
- return {
- editor: { tabSize: 2 },
- tailwindCSS: {
- inspectPort: null,
- emmetCompletions: false,
- classAttributes: ['class', 'className', 'ngClass', 'class:list'],
- codeActions: true,
- hovers: true,
- suggestions: true,
- validate: true,
- colorDecorators: true,
- rootFontSize: 16,
- lint: {
- cssConflict: 'warning',
- invalidApply: 'error',
- invalidScreen: 'error',
- invalidVariant: 'error',
- invalidConfigPath: 'error',
- invalidTailwindDirective: 'error',
- invalidSourceDirective: 'error',
- recommendedVariantOrder: 'warning',
- },
- showPixelEquivalents: true,
- includeLanguages: {},
- files: { exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'] },
- experimental: {
- classRegex: [],
- configFile: null,
- },
- },
- }
-}
-
export function createSettingsCache(connection: Connection): SettingsCache {
const cache: Map = new Map()
@@ -73,7 +42,7 @@ export function createSettingsCache(connection: Connection): SettingsCache {
tailwindCSS = isObject(tailwindCSS) ? tailwindCSS : {}
return merge(
- getDefaultSettings(),
+ getDefaultTailwindSettings(),
{ editor, tailwindCSS },
{ arrayMerge: (_destinationArray, sourceArray, _options) => sourceArray },
)
diff --git a/packages/tailwindcss-language-server/tests/utils/client.ts b/packages/tailwindcss-language-server/tests/utils/client.ts
index f7ee6e94..d6d317bb 100644
--- a/packages/tailwindcss-language-server/tests/utils/client.ts
+++ b/packages/tailwindcss-language-server/tests/utils/client.ts
@@ -44,7 +44,7 @@ import { createConfiguration, Configuration } from './configuration'
import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/util/getLanguageBoundaries'
import { DefaultMap } from '../../src/util/default-map'
import { connect, ConnectOptions } from './connection'
-import type { DeepPartial } from './types'
+import type { DeepPartial } from '@tailwindcss/language-service/src/types'
export interface DocumentDescriptor {
/**
diff --git a/packages/tailwindcss-language-server/tests/utils/configuration.ts b/packages/tailwindcss-language-server/tests/utils/configuration.ts
index 2b4d6fbc..8c08a518 100644
--- a/packages/tailwindcss-language-server/tests/utils/configuration.ts
+++ b/packages/tailwindcss-language-server/tests/utils/configuration.ts
@@ -1,4 +1,7 @@
-import type { Settings } from '@tailwindcss/language-service/src/util/state'
+import {
+ getDefaultTailwindSettings,
+ type Settings,
+} from '@tailwindcss/language-service/src/util/state'
import { URI } from 'vscode-uri'
import type { DeepPartial } from './types'
import { CacheMap } from '../../src/cache-map'
@@ -10,41 +13,7 @@ export interface Configuration {
}
export function createConfiguration(): Configuration {
- let defaults: Settings = {
- editor: {
- tabSize: 2,
- },
- tailwindCSS: {
- inspectPort: null,
- emmetCompletions: false,
- includeLanguages: {},
- classAttributes: ['class', 'className', 'ngClass', 'class:list'],
- suggestions: true,
- hovers: true,
- codeActions: true,
- validate: true,
- showPixelEquivalents: true,
- rootFontSize: 16,
- colorDecorators: true,
- lint: {
- cssConflict: 'warning',
- invalidApply: 'error',
- invalidScreen: 'error',
- invalidVariant: 'error',
- invalidConfigPath: 'error',
- invalidTailwindDirective: 'error',
- invalidSourceDirective: 'error',
- recommendedVariantOrder: 'warning',
- },
- experimental: {
- classRegex: [],
- configFile: {},
- },
- files: {
- exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'],
- },
- },
- }
+ let defaults = getDefaultTailwindSettings()
/**
* Settings per file or directory URI
diff --git a/packages/tailwindcss-language-server/tests/utils/types.ts b/packages/tailwindcss-language-server/tests/utils/types.ts
deleted file mode 100644
index 6be62396..00000000
--- a/packages/tailwindcss-language-server/tests/utils/types.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export type DeepPartial = {
- [P in keyof T]?: T[P] extends (infer U)[]
- ? U[]
- : T[P] extends (...args: any) => any
- ? T[P] | undefined
- : T[P] extends object
- ? DeepPartial
- : T[P]
-}
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index f1047879..d5fddaaa 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -41,9 +41,11 @@
},
"devDependencies": {
"@types/css.escape": "^1.5.2",
+ "@types/dedent": "^0.7.2",
"@types/line-column": "^1.0.2",
"@types/node": "^18.19.33",
"@types/stringify-object": "^4.0.5",
+ "dedent": "^1.5.3",
"esbuild": "^0.25.0",
"esbuild-node-externals": "^1.9.0",
"minimist": "^1.2.8",
diff --git a/packages/tailwindcss-language-service/scripts/build.mjs b/packages/tailwindcss-language-service/scripts/build.mjs
index 913debc3..128426be 100644
--- a/packages/tailwindcss-language-service/scripts/build.mjs
+++ b/packages/tailwindcss-language-service/scripts/build.mjs
@@ -30,7 +30,7 @@ let build = await esbuild.context({
// Call the tsc command to generate the types
spawnSync(
'tsc',
- ['--emitDeclarationOnly', '--outDir', path.resolve(__dirname, '../dist')],
+ ['-p', path.resolve(__dirname, './tsconfig.build.json'), '--emitDeclarationOnly', '--outDir', path.resolve(__dirname, '../dist')],
{
stdio: 'inherit',
},
diff --git a/packages/tailwindcss-language-service/scripts/tsconfig.build.json b/packages/tailwindcss-language-service/scripts/tsconfig.build.json
new file mode 100644
index 00000000..e80bb38f
--- /dev/null
+++ b/packages/tailwindcss-language-service/scripts/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "exclude": ["../src/**/*.test.ts"]
+}
\ No newline at end of file
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index c67eb0f5..fe43bb68 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -15,7 +15,7 @@ import removeMeta from './util/removeMeta'
import { formatColor, getColor, getColorFromValue } from './util/color'
import { isHtmlContext, isHtmlDoc, isVueDoc } from './util/html'
import { isCssContext } from './util/css'
-import { findLast, matchClassAttributes } from './util/find'
+import { findLast, matchClassAttributes, matchClassFunctions } from './util/find'
import { stringifyConfigValue, stringifyCss } from './util/stringify'
import { stringifyScreen, Screen } from './util/screens'
import isObject from './util/isObject'
@@ -45,6 +45,8 @@ import type { ThemeEntry } from './util/v4'
import { segment } from './util/segment'
import { resolveKnownThemeKeys, resolveKnownThemeNamespaces } from './util/v4/theme-keys'
import { SEARCH_RANGE } from './util/constants'
+import { getLanguageBoundaries } from './util/getLanguageBoundaries'
+import { isWithinRange } from './util/isWithinRange'
let isUtil = (className) =>
Array.isArray(className.__info)
@@ -747,6 +749,25 @@ async function provideClassAttributeCompletions(
let matches = matchClassAttributes(str, settings.classAttributes)
+ let boundaries = getLanguageBoundaries(state, document)
+
+ for (let boundary of boundaries ?? []) {
+ let isJsContext = boundary.type === 'js' || boundary.type === 'jsx'
+ if (!isJsContext) continue
+ if (!settings.classFunctions?.length) continue
+ if (!isWithinRange(position, boundary.range)) continue
+
+ let str = document.getText(boundary.range)
+ let offset = document.offsetAt(boundary.range.start)
+ let fnMatches = matchClassFunctions(str, settings.classFunctions)
+
+ fnMatches.forEach((match) => {
+ if (match.index) match.index += offset
+ })
+
+ matches.push(...fnMatches)
+ }
+
if (matches.length === 0) {
return null
}
diff --git a/packages/tailwindcss-language-service/src/types.ts b/packages/tailwindcss-language-service/src/types.ts
new file mode 100644
index 00000000..c84f0c7a
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/types.ts
@@ -0,0 +1,9 @@
+export type DeepPartial = {
+ [P in keyof T]?: T[P] extends ((...args: any) => any) | ReadonlyArray | Date
+ ? T[P]
+ : T[P] extends (infer U)[]
+ ? U[]
+ : T[P] extends object
+ ? DeepPartial
+ : T[P]
+}
diff --git a/packages/tailwindcss-language-service/src/util/find.test.ts b/packages/tailwindcss-language-service/src/util/find.test.ts
index 90dbcfb3..f9f17a96 100644
--- a/packages/tailwindcss-language-service/src/util/find.test.ts
+++ b/packages/tailwindcss-language-service/src/util/find.test.ts
@@ -1,81 +1,743 @@
-import type { State } from './state'
+import { createState, getDefaultTailwindSettings, Settings, type DocumentClassList } from './state'
import { test } from 'vitest'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { findClassListsInHtmlRange } from './find'
+import type { DeepPartial } from '../types'
+import dedent from 'dedent'
-test('test', async ({ expect }) => {
- let content = [
- //
- '',
- ' ',
- ' ',
- ].join('\n')
-
- let doc = TextDocument.create('file://file.astro', 'astro', 1, content)
- let state: State = {
- blocklist: [],
+const js = dedent
+const html = dedent
+
+test('class regex works in astro', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.astro',
+ lang: 'astro',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['class'],
+ experimental: {
+ classRegex: [
+ ['cva\\(([^)]*)\\)', '["\'`]([^"\'`]*).*?["\'`]'],
+ ['cn\\(([^)]*)\\)', '["\'`]([^"\'`]*).*?["\'`]'],
+ ],
+ },
+ },
+ },
+ content: [
+ '',
+ ' ',
+ ' ',
+ ],
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'html')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'p-4 sm:p-2 $',
+ range: {
+ start: { line: 0, character: 10 },
+ end: { line: 0, character: 22 },
+ },
+ },
+ {
+ classList: 'underline',
+ range: {
+ start: { line: 0, character: 33 },
+ end: { line: 0, character: 42 },
+ },
+ },
+ {
+ classList: 'line-through',
+ range: {
+ start: { line: 0, character: 46 },
+ end: { line: 0, character: 58 },
+ },
+ },
+ ])
+})
+
+test('find class lists in functions', async ({ expect }) => {
+ let fileA = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+ content: js`
+ // These should match
+ let classes = clsx(
+ 'flex p-4',
+ 'block sm:p-0',
+ Date.now() > 100 ? 'text-white' : 'text-black',
+ )
+
+ // These should match
+ let classes = cva(
+ 'flex p-4',
+ 'block sm:p-0',
+ Date.now() > 100 ? 'text-white' : 'text-black',
+ )
+ `,
+ })
+
+ let fileB = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+ content: js`
+ let classes = cn(
+ 'flex p-4',
+ 'block sm:p-0',
+ Date.now() > 100 ? 'text-white' : 'text-black',
+ )
+ `,
+ })
+
+ let classListsA = await findClassListsInHtmlRange(fileA.state, fileA.doc, 'js')
+ let classListsB = await findClassListsInHtmlRange(fileB.state, fileB.doc, 'js')
+
+ expect(classListsA).toEqual([
+ // from clsx(…)
+ {
+ classList: 'flex p-4',
+ range: {
+ start: { line: 2, character: 3 },
+ end: { line: 2, character: 11 },
+ },
+ },
+ {
+ classList: 'block sm:p-0',
+ range: {
+ start: { line: 3, character: 3 },
+ end: { line: 3, character: 15 },
+ },
+ },
+ {
+ classList: 'text-white',
+ range: {
+ start: { line: 4, character: 22 },
+ end: { line: 4, character: 32 },
+ },
+ },
+ {
+ classList: 'text-black',
+ range: {
+ start: { line: 4, character: 37 },
+ end: { line: 4, character: 47 },
+ },
+ },
+
+ // from cva(…)
+ {
+ classList: 'flex p-4',
+ range: {
+ start: { line: 9, character: 3 },
+ end: { line: 9, character: 11 },
+ },
+ },
+ {
+ classList: 'block sm:p-0',
+ range: {
+ start: { line: 10, character: 3 },
+ end: { line: 10, character: 15 },
+ },
+ },
+ {
+ classList: 'text-white',
+ range: {
+ start: { line: 11, character: 22 },
+ end: { line: 11, character: 32 },
+ },
+ },
+ {
+ classList: 'text-black',
+ range: {
+ start: { line: 11, character: 37 },
+ end: { line: 11, character: 47 },
+ },
+ },
+ ])
+
+ // none from cn(…) since it's not in the list of class functions
+ expect(classListsB).toEqual([])
+})
+
+test('find class lists in nested fn calls', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+
+ content: js`
+ // NOTE: All strings inside a matched class function will be treated as class lists
+ // TODO: Nested calls tha are *not* class functions should have their content ignored
+ let classes = clsx(
+ 'flex',
+ cn({
+ 'bg-red-500': true,
+ 'text-white': Date.now() > 100,
+ }),
+ clsx(
+ 'fixed',
+ 'absolute inset-0'
+ ),
+ cva(
+ ['bottom-0', 'border'],
+ {
+ variants: {
+ mobile: {
+ default: 'bottom-0 left-0',
+ large: \`
+ inset-0
+ rounded-none
+ \`,
+ },
+ }
+ }
+ )
+ )
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'html')
+
+ expect(classLists).toMatchObject([
+ {
+ classList: 'flex',
+ range: {
+ start: { line: 3, character: 3 },
+ end: { line: 3, character: 7 },
+ },
+ },
+
+ // TODO: This should be ignored because they're inside cn(…)
+ {
+ classList: 'bg-red-500',
+ range: {
+ start: { line: 5, character: 5 },
+ end: { line: 5, character: 15 },
+ },
+ },
+
+ // TODO: This should be ignored because they're inside cn(…)
+ {
+ classList: 'text-white',
+ range: {
+ start: { line: 6, character: 5 },
+ end: { line: 6, character: 15 },
+ },
+ },
+
+ {
+ classList: 'fixed',
+ range: {
+ start: { line: 9, character: 5 },
+ end: { line: 9, character: 10 },
+ },
+ },
+ {
+ classList: 'absolute inset-0',
+ range: {
+ start: { line: 10, character: 5 },
+ end: { line: 10, character: 21 },
+ },
+ },
+ {
+ classList: 'bottom-0',
+ range: {
+ start: { line: 13, character: 6 },
+ end: { line: 13, character: 14 },
+ },
+ },
+ {
+ classList: 'border',
+ range: {
+ start: { line: 13, character: 18 },
+ end: { line: 13, character: 24 },
+ },
+ },
+ {
+ classList: 'bottom-0 left-0',
+ range: {
+ start: { line: 17, character: 20 },
+ end: { line: 17, character: 35 },
+ },
+ },
+ {
+ classList: `inset-0\n rounded-none\n `,
+ range: {
+ start: { line: 19, character: 12 },
+ // TODO: Fix the range calculation. Its wrong on this one
+ end: { line: 20, character: 24 },
+ },
+ },
+ ])
+})
+
+test('find class lists in nested fn calls (only nested matches)', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+
+ content: js`
+ let classes = cn(
+ 'flex',
+ cn({
+ 'bg-red-500': true,
+ 'text-white': Date.now() > 100,
+ }),
+ // NOTE: The only class lists appear inside this function because cn is
+ // not in the list of class functions
+ clsx(
+ 'fixed',
+ 'absolute inset-0'
+ ),
+ )
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'html')
+
+ expect(classLists).toMatchObject([
+ {
+ classList: 'fixed',
+ range: {
+ start: { line: 9, character: 5 },
+ end: { line: 9, character: 10 },
+ },
+ },
+ {
+ classList: 'absolute inset-0',
+ range: {
+ start: { line: 10, character: 5 },
+ end: { line: 10, character: 21 },
+ },
+ },
+ ])
+})
+
+test('find class lists in tagged template literals', async ({ expect }) => {
+ let fileA = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+ content: js`
+ // These should match
+ let classes = clsx\`
+ flex p-4
+ block sm:p-0
+ \${Date.now() > 100 ? 'text-white' : 'text-black'}
+ \`
+
+ // These should match
+ let classes = cva\`
+ flex p-4
+ block sm:p-0
+ \${Date.now() > 100 ? 'text-white' : 'text-black'}
+ \`
+ `,
+ })
+
+ let fileB = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx', 'cva'],
+ },
+ },
+ content: js`
+ let classes = cn\`
+ flex p-4
+ block sm:p-0
+ \${Date.now() > 100 ? 'text-white' : 'text-black'}
+ \`
+ `,
+ })
+
+ let classListsA = await findClassListsInHtmlRange(fileA.state, fileA.doc, 'js')
+ let classListsB = await findClassListsInHtmlRange(fileB.state, fileB.doc, 'js')
+
+ expect(classListsA).toEqual([
+ // from clsx`…`
+ {
+ classList: 'flex p-4\n block sm:p-0\n $',
+ range: {
+ start: { line: 2, character: 2 },
+ end: { line: 4, character: 3 },
+ },
+ },
+ {
+ classList: 'text-white',
+ range: {
+ start: { line: 4, character: 24 },
+ end: { line: 4, character: 34 },
+ },
+ },
+ {
+ classList: 'text-black',
+ range: {
+ start: { line: 4, character: 39 },
+ end: { line: 4, character: 49 },
+ },
+ },
+
+ // from cva`…`
+ {
+ classList: 'flex p-4\n block sm:p-0\n $',
+ range: {
+ start: { line: 9, character: 2 },
+ end: { line: 11, character: 3 },
+ },
+ },
+ {
+ classList: 'text-white',
+ range: {
+ start: { line: 11, character: 24 },
+ end: { line: 11, character: 34 },
+ },
+ },
+ {
+ classList: 'text-black',
+ range: {
+ start: { line: 11, character: 39 },
+ end: { line: 11, character: 49 },
+ },
+ },
+ ])
+
+ // none from cn`…` since it's not in the list of class functions
+ expect(classListsB).toEqual([])
+})
+
+test('classFunctions can be a regex', async ({ expect }) => {
+ let fileA = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['tw\\.[a-z]+'],
+ },
+ },
+ content: js`
+ let classes = tw.div('flex p-4')
+ `,
+ })
+
+ let fileB = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['tw\\.[a-z]+'],
+ },
+ },
+ content: js`
+ let classes = tw.div.foo('flex p-4')
+ `,
+ })
+
+ let classListsA = await findClassListsInHtmlRange(fileA.state, fileA.doc, 'js')
+ let classListsB = await findClassListsInHtmlRange(fileB.state, fileB.doc, 'js')
+
+ expect(classListsA).toEqual([
+ {
+ classList: 'flex p-4',
+ range: {
+ start: { line: 0, character: 22 },
+ end: { line: 0, character: 30 },
+ },
+ },
+ ])
+
+ // none from tw.div.foo(`…`) since it does not match a class function
+ expect(classListsB).toEqual([])
+})
+
+test('classFunctions regexes only match on function names', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ // A function name itself cannot contain a `:`
+ classFunctions: [':\\s*tw\\.[a-z]+'],
+ },
+ },
+ content: js`
+ let classes = tw.div('flex p-4')
+ let classes = { foo: tw.div('flex p-4') }
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([])
+})
+
+test('Finds consecutive instances of a class function', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.js',
+ lang: 'javascript',
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['cn'],
+ },
+ },
+ content: js`
+ export const list = [
+ cn('relative flex bg-red-500'),
+ cn('relative flex bg-red-500'),
+ cn('relative flex bg-red-500'),
+ ]
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex bg-red-500',
+ range: {
+ start: { line: 1, character: 6 },
+ end: { line: 1, character: 30 },
+ },
+ },
+ {
+ classList: 'relative flex bg-red-500',
+ range: {
+ start: { line: 2, character: 6 },
+ end: { line: 2, character: 30 },
+ },
+ },
+ {
+ classList: 'relative flex bg-red-500',
+ range: {
+ start: { line: 3, character: 6 },
+ end: { line: 3, character: 30 },
+ },
+ },
+ ])
+})
+
+test('classFunctions & classAttributes should not duplicate matches', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.jsx',
+ lang: 'javascriptreact',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['className'],
+ classFunctions: ['cva', 'clsx'],
+ },
+ },
+ content: js`
+ const Component = ({ className }) => (
+
+ CONTENT
+
+ )
+ const OtherComponent = ({ className }) => (
+
+ CONTENT
+
+ )
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 3, character: 7 },
+ end: { line: 3, character: 20 },
+ },
+ },
+ {
+ classList: 'inset-0 md:h-[calc(100%-2rem)]',
+ range: {
+ start: { line: 4, character: 7 },
+ end: { line: 4, character: 37 },
+ },
+ },
+ {
+ classList: 'rounded-none bg-blue-700',
+ range: {
+ start: { line: 5, character: 12 },
+ end: { line: 5, character: 36 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 14, character: 7 },
+ end: { line: 14, character: 20 },
+ },
+ },
+ {
+ classList: 'inset-0 md:h-[calc(100%-2rem)]',
+ range: {
+ start: { line: 15, character: 7 },
+ end: { line: 15, character: 37 },
+ },
+ },
+ {
+ classList: 'rounded-none bg-blue-700',
+ range: {
+ start: { line: 16, character: 12 },
+ end: { line: 16, character: 36 },
+ },
+ },
+ ])
+})
+
+test('classFunctions should only match in JS-like contexts', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.html',
+ lang: 'html',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['className'],
+ classFunctions: ['clsx'],
+ },
+ },
+ content: html`
+
+ clsx('relative flex') clsx('relative flex')
+
+
+
+
+
+ clsx('relative flex') clsx('relative flex')
+
+
+
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 5, character: 16 },
+ end: { line: 5, character: 29 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 6, character: 16 },
+ end: { line: 6, character: 29 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 14, character: 16 },
+ end: { line: 14, character: 29 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 15, character: 16 },
+ end: { line: 15, character: 29 },
+ },
+ },
+ ])
+})
+
+function createDocument({
+ name,
+ lang,
+ content,
+ settings,
+}: {
+ name: string
+ lang: string
+ content: string | string[]
+ settings: DeepPartial
+}) {
+ let doc = TextDocument.create(
+ `file://${name}`,
+ lang,
+ 1,
+ typeof content === 'string' ? content : content.join('\n'),
+ )
+ let defaults = getDefaultTailwindSettings()
+ let state = createState({
editor: {
- userLanguages: {},
getConfiguration: async () => ({
- editor: {
- tabSize: 1,
- },
+ ...defaults,
+ ...settings,
tailwindCSS: {
- classAttributes: ['class'],
- experimental: {
- classRegex: [
- ['cva\\(([^)]*)\\)', '["\'`]([^"\'`]*).*?["\'`]'],
- ['cn\\(([^)]*)\\)', '["\'`]([^"\'`]*).*?["\'`]'],
- ],
- },
- } as any,
- }),
- } as any,
- } as any
-
- let classLists = await findClassListsInHtmlRange(state, doc, 'html')
-
- expect(classLists).toMatchInlineSnapshot(`
- [
- {
- "classList": "p-4 sm:p-2 $",
- "range": {
- "end": {
- "character": 22,
- "line": 0,
+ ...defaults.tailwindCSS,
+ ...settings.tailwindCSS,
+ lint: {
+ ...defaults.tailwindCSS.lint,
+ ...(settings.tailwindCSS?.lint ?? {}),
},
- "start": {
- "character": 10,
- "line": 0,
- },
- },
- },
- {
- "classList": "underline",
- "range": {
- "end": {
- "character": 42,
- "line": 0,
+ experimental: {
+ ...defaults.tailwindCSS.experimental,
+ ...(settings.tailwindCSS?.experimental ?? {}),
},
- "start": {
- "character": 33,
- "line": 0,
+ files: {
+ ...defaults.tailwindCSS.files,
+ ...(settings.tailwindCSS?.files ?? {}),
},
},
- },
- {
- "classList": "line-through",
- "range": {
- "end": {
- "character": 58,
- "line": 0,
- },
- "start": {
- "character": 46,
- "line": 0,
- },
+ editor: {
+ ...defaults.editor,
+ ...settings.editor,
},
- },
- ]
- `)
-})
+ }),
+ },
+ })
+
+ return {
+ doc,
+ state,
+ }
+}
diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts
index f38d7a16..03218798 100644
--- a/packages/tailwindcss-language-service/src/util/find.ts
+++ b/packages/tailwindcss-language-service/src/util/find.ts
@@ -9,7 +9,7 @@ import { isJsxContext } from './js'
import { dedupeByRange, flatten } from './array'
import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers'
import { getLanguageBoundaries } from './getLanguageBoundaries'
-import { resolveRange } from './resolveRange'
+import { absoluteRange } from './absoluteRange'
import { getTextWithoutComments } from './doc'
import { isSemicolonlessCssLanguage } from './languages'
import { customClassesIn } from './classes'
@@ -164,20 +164,66 @@ export function matchClassAttributes(text: string, attributes: string[]): RegExp
return findAll(new RegExp(re.source.replace('ATTRS', attrs.join('|')), 'gi'), text)
}
+export function matchClassFunctions(text: string, fnNames: string[]): RegExpMatchArray[] {
+ // 1. Validate the list of function name patterns provided by the user
+ let names = fnNames.filter((x) => typeof x === 'string')
+ if (names.length === 0) return []
+
+ // 2. Extract function names in the document
+ // This is intentionally scoped to JS syntax for now but should be extended to
+ // other languages in the future
+ //
+ // This regex the JS pattern for an identifier + function call with some
+ // additional constraints:
+ //
+ // - It needs to be in an expression position — so it must be preceded by
+ // whitespace, parens, curlies, commas, whitespace, etc…
+ // - It must look like a fn call or a tagged template literal
+ let FN_NAMES = /(?<=^|[:=,;\s{()])([\p{ID_Start}$_][\p{ID_Continue}$_.]*)[(`]/dgiu
+ let foundFns = findAll(FN_NAMES, text)
+
+ // 3. Match against the function names in the document
+ let re = /^(NAMES)$/
+ let isClassFn = new RegExp(re.source.replace('NAMES', names.join('|')), 'i')
+
+ let matches = foundFns.filter((fn) => isClassFn.test(fn[1]))
+
+ return matches
+}
+
export async function findClassListsInHtmlRange(
state: State,
doc: TextDocument,
type: 'html' | 'js' | 'jsx',
range?: Range,
): Promise {
+ if (!state.editor) return []
+
const text = getTextWithoutComments(doc, type, range)
- const matches = matchClassAttributes(
- text,
- (await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes,
- )
+ const settings = (await state.editor.getConfiguration(doc.uri)).tailwindCSS
+ const matches = matchClassAttributes(text, settings.classAttributes)
- const result: DocumentClassList[] = []
+ let boundaries = getLanguageBoundaries(state, doc)
+
+ for (let boundary of boundaries ?? []) {
+ let isJsContext = boundary.type === 'js' || boundary.type === 'jsx'
+ if (!isJsContext) continue
+ if (!settings.classFunctions?.length) continue
+
+ let str = doc.getText(boundary.range)
+ let offset = doc.offsetAt(boundary.range.start)
+ let fnMatches = matchClassFunctions(str, settings.classFunctions)
+
+ fnMatches.forEach((match) => {
+ if (match.index) match.index += offset
+ })
+
+ matches.push(...fnMatches)
+ }
+
+ const existingResultSet = new Set()
+ const results: DocumentClassList[] = []
matches.forEach((match) => {
const subtext = text.substr(match.index + match[0].length - 1)
@@ -222,46 +268,53 @@ export async function findClassListsInHtmlRange(
})
}
- result.push(
- ...classLists
- .map(({ value, offset }) => {
- if (value.trim() === '') {
- return null
- }
+ classLists.forEach(({ value, offset }) => {
+ if (value.trim() === '') {
+ return null
+ }
- const before = value.match(/^\s*/)
- const beforeOffset = before === null ? 0 : before[0].length
- const after = value.match(/\s*$/)
- const afterOffset = after === null ? 0 : -after[0].length
-
- const start = indexToPosition(
- text,
- match.index + match[0].length - 1 + offset + beforeOffset,
- )
- const end = indexToPosition(
- text,
- match.index + match[0].length - 1 + offset + value.length + afterOffset,
- )
-
- return {
- classList: value.substr(beforeOffset, value.length + afterOffset),
- range: {
- start: {
- line: (range?.start.line || 0) + start.line,
- character: (end.line === 0 ? range?.start.character || 0 : 0) + start.character,
- },
- end: {
- line: (range?.start.line || 0) + end.line,
- character: (end.line === 0 ? range?.start.character || 0 : 0) + end.character,
- },
- },
- }
- })
- .filter((x) => x !== null),
- )
+ const before = value.match(/^\s*/)
+ const beforeOffset = before === null ? 0 : before[0].length
+ const after = value.match(/\s*$/)
+ const afterOffset = after === null ? 0 : -after[0].length
+
+ const start = indexToPosition(text, match.index + match[0].length - 1 + offset + beforeOffset)
+ const end = indexToPosition(
+ text,
+ match.index + match[0].length - 1 + offset + value.length + afterOffset,
+ )
+
+ const result: DocumentClassList = {
+ classList: value.substr(beforeOffset, value.length + afterOffset),
+ range: {
+ start: {
+ line: (range?.start.line || 0) + start.line,
+ character: (end.line === 0 ? range?.start.character || 0 : 0) + start.character,
+ },
+ end: {
+ line: (range?.start.line || 0) + end.line,
+ character: (end.line === 0 ? range?.start.character || 0 : 0) + end.character,
+ },
+ },
+ }
+
+ const resultKey = [
+ result.classList,
+ result.range.start.line,
+ result.range.start.character,
+ result.range.end.line,
+ result.range.end.character,
+ ].join(':')
+
+ // No need to add the result if it was already matched
+ if (!existingResultSet.has(resultKey)) {
+ existingResultSet.add(resultKey)
+ results.push(result)
+ }
+ })
})
- return result
+ return results
}
export async function findClassListsInRange(
@@ -407,14 +460,14 @@ export function findHelperFunctionsInRange(
helper,
path,
ranges: {
- full: resolveRange(
+ full: absoluteRange(
{
start: indexToPosition(text, startIndex),
end: indexToPosition(text, startIndex + match.groups.path.length),
},
range,
),
- path: resolveRange(
+ path: absoluteRange(
{
start: indexToPosition(text, startIndex + quotesBefore.length),
end: indexToPosition(text, startIndex + quotesBefore.length + path.length),
diff --git a/packages/tailwindcss-language-service/src/util/lexers.ts b/packages/tailwindcss-language-service/src/util/lexers.ts
index 38ecc2d7..a8315f51 100644
--- a/packages/tailwindcss-language-service/src/util/lexers.ts
+++ b/packages/tailwindcss-language-service/src/util/lexers.ts
@@ -29,6 +29,14 @@ const classAttributeStates: () => { [x: string]: moo.Rules } = () => ({
rbrace: { match: new RegExp('(? {
start2: { match: "'", push: 'singleClassList' },
start3: { match: '{', push: 'interpBrace' },
start4: { match: '`', push: 'tickClassList' },
+ start5: { match: '(', push: 'interpParen' },
},
...classAttributeStates(),
})
diff --git a/packages/tailwindcss-language-service/src/util/resolveRange.ts b/packages/tailwindcss-language-service/src/util/resolveRange.ts
deleted file mode 100644
index d90fa5b9..00000000
--- a/packages/tailwindcss-language-service/src/util/resolveRange.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { Range } from 'vscode-languageserver'
-
-export function resolveRange(range: Range, relativeTo?: Range) {
- return {
- start: {
- line: (relativeTo?.start.line || 0) + range.start.line,
- character:
- (range.end.line === 0 ? relativeTo?.start.character || 0 : 0) + range.start.character,
- },
- end: {
- line: (relativeTo?.start.line || 0) + range.end.line,
- character:
- (range.end.line === 0 ? relativeTo?.start.character || 0 : 0) + range.end.character,
- },
- }
-}
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 95afe8ec..3bdb1bc4 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -46,6 +46,7 @@ export type TailwindCssSettings = {
emmetCompletions: boolean
includeLanguages: Record
classAttributes: string[]
+ classFunctions: string[]
suggestions: boolean
hovers: boolean
codeActions: boolean
@@ -64,7 +65,7 @@ export type TailwindCssSettings = {
recommendedVariantOrder: DiagnosticSeveritySetting
}
experimental: {
- classRegex: string[]
+ classRegex: string[] | [string, string][]
configFile: string | Record | null
}
files: {
@@ -171,3 +172,73 @@ export type ClassNameMeta = {
scope: string[]
context: string[]
}
+
+/**
+ * @internal
+ */
+export function getDefaultTailwindSettings(): Settings {
+ return {
+ editor: { tabSize: 2 },
+ tailwindCSS: {
+ inspectPort: null,
+ emmetCompletions: false,
+ classAttributes: ['class', 'className', 'ngClass', 'class:list'],
+ classFunctions: [],
+ codeActions: true,
+ hovers: true,
+ suggestions: true,
+ validate: true,
+ colorDecorators: true,
+ rootFontSize: 16,
+ lint: {
+ cssConflict: 'warning',
+ invalidApply: 'error',
+ invalidScreen: 'error',
+ invalidVariant: 'error',
+ invalidConfigPath: 'error',
+ invalidTailwindDirective: 'error',
+ invalidSourceDirective: 'error',
+ recommendedVariantOrder: 'warning',
+ },
+ showPixelEquivalents: true,
+ includeLanguages: {},
+ files: { exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'] },
+ experimental: {
+ classRegex: [],
+ configFile: null,
+ },
+ },
+ }
+}
+
+/**
+ * @internal
+ */
+export function createState(
+ partial: Omit, 'editor'> & {
+ editor?: Partial
+ },
+): State {
+ return {
+ enabled: true,
+ ...partial,
+ editor: {
+ get connection(): Connection {
+ throw new Error('Not implemented')
+ },
+ folder: '/',
+ userLanguages: {},
+ capabilities: {
+ configuration: true,
+ diagnosticRelatedInformation: true,
+ itemDefaults: [],
+ },
+ getConfiguration: () => {
+ throw new Error('Not implemented')
+ },
+ getDocumentSymbols: async () => [],
+ readDirectory: async () => [],
+ ...partial.editor,
+ },
+ }
+}
diff --git a/packages/tailwindcss-language-service/tsconfig.json b/packages/tailwindcss-language-service/tsconfig.json
index 605ece3c..883356e7 100644
--- a/packages/tailwindcss-language-service/tsconfig.json
+++ b/packages/tailwindcss-language-service/tsconfig.json
@@ -1,6 +1,5 @@
{
"include": ["src", "../../types"],
- "exclude": ["src/**/*.test.ts"],
"compilerOptions": {
"module": "NodeNext",
"lib": ["ES2022"],
@@ -15,6 +14,7 @@
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "NodeNext",
+ "skipLibCheck": true,
"jsx": "react",
"esModuleInterop": true
}
diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md
index 62165308..efc937cc 100644
--- a/packages/vscode-tailwindcss/README.md
+++ b/packages/vscode-tailwindcss/README.md
@@ -90,6 +90,26 @@ Enable completions when using [Emmet](https://emmet.io/)-style syntax, for examp
The HTML attributes for which to provide class completions, hover previews, linting etc. **Default: `class`, `className`, `ngClass`, `class:list`**
+### `tailwindCSS.classFunctions`
+
+Functions in which to provide completions, hover previews, linting etc. Currently, this works for both function calls and tagged template literals in JavaScript / TypeScript.
+
+Example:
+
+```json
+{
+ "tailwindCSS.classFunctions": ["tw", "clsx"]
+}
+```
+
+```javascript
+let classes = tw`flex bg-red-500`
+let classes2 = clsx([
+ "flex bg-red-500",
+ { "text-red-500": true }
+])
+```
+
### `tailwindCSS.colorDecorators`
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions. **Default: `true`**
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 3b5fd76a..036ca936 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -184,6 +184,14 @@
],
"markdownDescription": "The HTML attributes for which to provide class completions, hover previews, linting etc."
},
+ "tailwindCSS.classFunctions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "default": [],
+ "markdownDescription": "The function or tagged template literal names for which to provide class completions, hover previews, linting etc."
+ },
"tailwindCSS.suggestions": {
"type": "boolean",
"default": true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f1048aaa..2378a376 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -300,6 +300,9 @@ importers:
'@types/css.escape':
specifier: ^1.5.2
version: 1.5.2
+ '@types/dedent':
+ specifier: ^0.7.2
+ version: 0.7.2
'@types/line-column':
specifier: ^1.0.2
version: 1.0.2
@@ -309,6 +312,9 @@ importers:
'@types/stringify-object':
specifier: ^4.0.5
version: 4.0.5
+ dedent:
+ specifier: ^1.5.3
+ version: 1.5.3
esbuild:
specifier: ^0.25.0
version: 0.25.0
@@ -969,6 +975,9 @@ packages:
'@types/debounce@1.2.0':
resolution: {integrity: sha512-bWG5wapaWgbss9E238T0R6bfo5Fh3OkeoSt245CM7JJwVwpw6MEBCbIxLq5z8KzsE3uJhzcIuQkyiZmzV3M/Dw==}
+ '@types/dedent@0.7.2':
+ resolution: {integrity: sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw==}
+
'@types/dlv@1.1.4':
resolution: {integrity: sha512-m8KmImw4Jt+4rIgupwfivrWEOnj1LzkmKkqbh075uG13eTQ1ZxHWT6T0vIdSQhLIjQCiR0n0lZdtyDOPO1x2Mw==}
@@ -3227,6 +3236,8 @@ snapshots:
'@types/debounce@1.2.0': {}
+ '@types/dedent@0.7.2': {}
+
'@types/dlv@1.1.4': {}
'@types/estree@1.0.6': {}
From b68a681ac748a4669ad3f4287a40b8c5f9a62c95 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 19 Mar 2025 09:57:07 -0400
Subject: [PATCH 015/108] Update changelog
---
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index ca4255d7..dd3aa562 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258))
# 0.14.9
From 0d9bd2e8486a5cd3276fa47bd9a2749537e6a566 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 19 Mar 2025 10:01:32 -0400
Subject: [PATCH 016/108] v4: Show completions after any valid variant (#1263)
Fixes #943
Fixes #1257
This doesn't ensure variants like `not-supports-*` are suggested because
`not-supports:` isn't valid. Only something with a value is e.g.
`not-supports-display`. Making that work (it really should) is a larger
task and requires designing an interactive suggestion API for v4.
---
.../tests/completions/completions.test.js | 70 +++++++++++++++++-
.../src/completionProvider.ts | 27 -------
.../src/util/getVariantsFromClassName.ts | 73 +++++++++----------
.../src/util/v4/design-system.ts | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
5 files changed, 104 insertions(+), 69 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index 2727fcb0..f5393a40 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -1,5 +1,7 @@
-import { test } from 'vitest'
+import { test, expect, describe } from 'vitest'
import { withFixture } from '../common'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
function buildCompletion(c) {
return async function completion({
@@ -670,3 +672,69 @@ withFixture('v4/workspaces', (c) => {
})
})
})
+
+defineTest({
+ name: 'v4: Completions show after a variant arbitrary value',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 23 })
+
+ expect(completion?.items.length).toBe(12289)
+ },
+})
+
+defineTest({
+ name: 'v4: Completions show after an arbitrary variant',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 22 })
+
+ expect(completion?.items.length).toBe(12289)
+ },
+})
+
+defineTest({
+ name: 'v4: Completions show after a variant with a bare value',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 31 })
+
+ expect(completion?.items.length).toBe(12289)
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index fe43bb68..1579fd93 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -189,16 +189,8 @@ export function completionsFromClassList(
}),
)
} else {
- let shouldSortVariants = !semver.gte(state.version, '2.99.0')
let resultingVariants = [...existingVariants, variant.name]
- if (shouldSortVariants) {
- let allVariants = state.variants.map(({ name }) => name)
- resultingVariants = resultingVariants.sort(
- (a, b) => allVariants.indexOf(b) - allVariants.indexOf(a),
- )
- }
-
let selectors: string[] = []
try {
@@ -223,25 +215,6 @@ export function completionsFromClassList(
.map((selector) => addPixelEquivalentsToMediaQuery(selector))
.join(', '),
textEditText: resultingVariants[resultingVariants.length - 1] + sep,
- additionalTextEdits:
- shouldSortVariants && resultingVariants.length > 1
- ? [
- {
- newText:
- resultingVariants.slice(0, resultingVariants.length - 1).join(sep) + sep,
- range: {
- start: {
- ...classListRange.start,
- character: classListRange.end.character - partialClassName.length,
- },
- end: {
- ...replacementRange.start,
- character: replacementRange.start.character,
- },
- },
- },
- ]
- : [],
}),
)
}
diff --git a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
index b58bb29f..c30e729a 100644
--- a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
+++ b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
@@ -1,5 +1,6 @@
import type { State } from './state'
import * as jit from './jit'
+import { segment } from './segment'
export function getVariantsFromClassName(
state: State,
@@ -13,60 +14,52 @@ export function getVariantsFromClassName(
}
return [variant.name]
})
- let variants = new Set
()
- let offset = 0
- let parts = splitAtTopLevelOnly(className, state.separator)
+
+ let parts = segment(className, state.separator)
if (parts.length < 2) {
- return { variants: Array.from(variants), offset }
+ return { variants: [], offset: 0 }
}
+
parts = parts.filter(Boolean)
- for (let part of parts) {
- if (
- allVariants.includes(part) ||
- (state.jit &&
- ((part.includes('[') && part.endsWith(']')) || part.includes('/')) &&
- jit.generateRules(state, [`${part}${state.separator}[color:red]`]).rules.length > 0)
- ) {
- variants.add(part)
- offset += part.length + state.separator.length
- continue
+ function isValidVariant(part: string) {
+ if (allVariants.includes(part)) {
+ return true
}
- break
- }
+ let className = `${part}${state.separator}[color:red]`
- return { variants: Array.from(variants), offset }
-}
+ if (state.v4) {
+ // NOTE: This should never happen
+ if (!state.designSystem) return false
-// https://github.com/tailwindlabs/tailwindcss/blob/a8a2e2a7191fbd4bee044523aecbade5823a8664/src/util/splitAtTopLevelOnly.js
-function splitAtTopLevelOnly(input: string, separator: string): string[] {
- let stack: string[] = []
- let parts: string[] = []
- let lastPos = 0
+ // We don't use `compile()` so there's no overhead from PostCSS
+ let compiled = state.designSystem.candidatesToCss([className])
- for (let idx = 0; idx < input.length; idx++) {
- let char = input[idx]
+ // NOTE: This should never happen
+ if (compiled.length !== 1) return false
- if (stack.length === 0 && char === separator[0]) {
- if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
- parts.push(input.slice(lastPos, idx))
- lastPos = idx + separator.length
- }
+ return compiled[0] !== null
}
- if (char === '(' || char === '[' || char === '{') {
- stack.push(char)
- } else if (
- (char === ')' && stack[stack.length - 1] === '(') ||
- (char === ']' && stack[stack.length - 1] === '[') ||
- (char === '}' && stack[stack.length - 1] === '{')
- ) {
- stack.pop()
+ if (state.jit) {
+ if ((part.includes('[') && part.endsWith(']')) || part.includes('/')) {
+ return jit.generateRules(state, [className]).rules.length > 0
+ }
}
+
+ return false
}
- parts.push(input.slice(lastPos))
+ let offset = 0
+ let variants = new Set()
- return parts
+ for (let part of parts) {
+ if (!isValidVariant(part)) break
+
+ variants.add(part)
+ offset += part.length + state.separator!.length
+ }
+
+ return { variants: Array.from(variants), offset }
}
diff --git a/packages/tailwindcss-language-service/src/util/v4/design-system.ts b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
index cce64d4b..3fb3c401 100644
--- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
@@ -44,6 +44,6 @@ export interface DesignSystem {
export interface DesignSystem {
dependencies(): Set
- compile(classes: string[]): postcss.Root[]
+ compile(classes: string[]): (postcss.Root | null)[]
toCss(nodes: postcss.Root | postcss.Node[]): string
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index dd3aa562..7b9e68fa 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258))
+- v4: Make sure completions show after variants using arbitrary and bare values ([#1263](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1263))
# 0.14.9
From 9f9208af592347924461f8a2c8ac26a09ccb14e3 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 19 Mar 2025 10:25:34 -0400
Subject: [PATCH 017/108] =?UTF-8?q?Add=20support=20for=20`@source=20not`?=
=?UTF-8?q?=20and=20`@source=20inline(=E2=80=A6)`=20(#1262)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
IntelliSense counterpart of
https://github.com/tailwindlabs/tailwindcss/pull/17147
---
.../tailwindcss-language-server/package.json | 1 +
.../src/projects.ts | 35 +++++-
.../tailwindcss-language-server/src/tw.ts | 13 +++
.../tests/code-lens/source-inline.test.ts | 101 ++++++++++++++++++
.../tests/completions/at-config.test.js | 97 +++++++++++++++++
.../document-links/document-links.test.js | 38 +++++++
.../tests/hover/hover.test.js | 95 ++++++++++++++++
.../tests/utils/client.ts | 26 +++++
.../tailwindcss-language-service/package.json | 1 +
.../src/codeLensProvider.ts | 78 ++++++++++++++
.../src/completionProvider.ts | 13 +++
.../src/completions/file-paths.test.ts | 16 +++
.../src/completions/file-paths.ts | 10 +-
.../getInvalidSourceDiagnostics.ts | 7 +-
.../src/documentLinksProvider.ts | 2 +-
.../src/features.ts | 20 ++--
.../src/hoverProvider.ts | 27 +++--
.../src/util/estimated-class-size.ts | 35 ++++++
.../src/util/format-bytes.ts | 11 ++
.../src/util/state.ts | 5 +
packages/vscode-tailwindcss/CHANGELOG.md | 2 +
packages/vscode-tailwindcss/package.json | 6 ++
pnpm-lock.yaml | 6 ++
23 files changed, 624 insertions(+), 21 deletions(-)
create mode 100644 packages/tailwindcss-language-server/tests/code-lens/source-inline.test.ts
create mode 100644 packages/tailwindcss-language-service/src/codeLensProvider.ts
create mode 100644 packages/tailwindcss-language-service/src/util/estimated-class-size.ts
create mode 100644 packages/tailwindcss-language-service/src/util/format-bytes.ts
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 5ed431fb..fa0a7e91 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -42,6 +42,7 @@
"@tailwindcss/line-clamp": "0.4.2",
"@tailwindcss/oxide": "^4.0.0-alpha.19",
"@tailwindcss/typography": "0.5.7",
+ "@types/braces": "3.0.1",
"@types/color-name": "^1.1.3",
"@types/culori": "^2.1.0",
"@types/debounce": "1.2.0",
diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index b55ee078..afc4515f 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -15,6 +15,8 @@ import type {
Disposable,
DocumentLinkParams,
DocumentLink,
+ CodeLensParams,
+ CodeLens,
} from 'vscode-languageserver/node'
import { FileChangeType } from 'vscode-languageserver/node'
import type { TextDocument } from 'vscode-languageserver-textdocument'
@@ -35,6 +37,7 @@ import stackTrace from 'stack-trace'
import extractClassNames from './lib/extractClassNames'
import { klona } from 'klona/full'
import { doHover } from '@tailwindcss/language-service/src/hoverProvider'
+import { getCodeLens } from '@tailwindcss/language-service/src/codeLensProvider'
import { Resolver } from './resolver'
import {
doComplete,
@@ -110,6 +113,7 @@ export interface ProjectService {
onColorPresentation(params: ColorPresentationParams): Promise
onCodeAction(params: CodeActionParams): Promise
onDocumentLinks(params: DocumentLinkParams): Promise
+ onCodeLens(params: CodeLensParams): Promise
sortClassLists(classLists: string[]): string[]
dependencies(): Iterable
@@ -212,6 +216,7 @@ export async function createProjectService(
let state: State = {
enabled: false,
+ features: [],
completionItemData: {
_projectKey: projectKey,
},
@@ -462,6 +467,14 @@ export async function createProjectService(
// and this should be determined there and passed in instead
let features = supportedFeatures(tailwindcssVersion, tailwindcss)
log(`supported features: ${JSON.stringify(features)}`)
+ state.features = features
+
+ if (params.initializationOptions?.testMode) {
+ state.features = [
+ ...state.features,
+ ...(params.initializationOptions.additionalFeatures ?? []),
+ ]
+ }
if (!features.includes('css-at-theme')) {
tailwindcss = tailwindcss.default ?? tailwindcss
@@ -688,6 +701,15 @@ export async function createProjectService(
state.v4 = true
state.v4Fallback = true
state.jit = true
+ state.features = features
+
+ if (params.initializationOptions?.testMode) {
+ state.features = [
+ ...state.features,
+ ...(params.initializationOptions.additionalFeatures ?? []),
+ ]
+ }
+
state.modules = {
tailwindcss: { version: tailwindcssVersion, module: tailwindcss },
postcss: { version: null, module: null },
@@ -1150,7 +1172,7 @@ export async function createProjectService(
},
tryInit,
async dispose() {
- state = { enabled: false }
+ state = { enabled: false, features: [] }
for (let disposable of disposables) {
;(await disposable).dispose()
}
@@ -1177,6 +1199,17 @@ export async function createProjectService(
return doHover(state, document, params.position)
}, null)
},
+ async onCodeLens(params: CodeLensParams): Promise {
+ return withFallback(async () => {
+ if (!state.enabled) return null
+ let document = documentService.getDocument(params.textDocument.uri)
+ if (!document) return null
+ let settings = await state.editor.getConfiguration(document.uri)
+ if (!settings.tailwindCSS.codeLens) return null
+ if (await isExcluded(state, document)) return null
+ return getCodeLens(state, document)
+ }, null)
+ },
async onCompletion(params: CompletionParams): Promise {
return withFallback(async () => {
if (!state.enabled) return null
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index 35da9386..fc6a87a8 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -19,6 +19,8 @@ import type {
DocumentLink,
InitializeResult,
WorkspaceFolder,
+ CodeLensParams,
+ CodeLens,
} from 'vscode-languageserver/node'
import {
CompletionRequest,
@@ -30,6 +32,7 @@ import {
FileChangeType,
DocumentLinkRequest,
TextDocumentSyncKind,
+ CodeLensRequest,
} from 'vscode-languageserver/node'
import { URI } from 'vscode-uri'
import normalizePath from 'normalize-path'
@@ -757,6 +760,7 @@ export class TW {
this.connection.onDocumentColor(this.onDocumentColor.bind(this))
this.connection.onColorPresentation(this.onColorPresentation.bind(this))
this.connection.onCodeAction(this.onCodeAction.bind(this))
+ this.connection.onCodeLens(this.onCodeLens.bind(this))
this.connection.onDocumentLinks(this.onDocumentLinks.bind(this))
this.connection.onRequest(this.onRequest.bind(this))
}
@@ -809,6 +813,7 @@ export class TW {
capabilities.add(HoverRequest.type, { documentSelector: null })
capabilities.add(DocumentColorRequest.type, { documentSelector: null })
capabilities.add(CodeActionRequest.type, { documentSelector: null })
+ capabilities.add(CodeLensRequest.type, { documentSelector: null })
capabilities.add(DocumentLinkRequest.type, { documentSelector: null })
capabilities.add(CompletionRequest.type, {
@@ -931,6 +936,11 @@ export class TW {
return this.getProject(params.textDocument)?.onCodeAction(params) ?? null
}
+ async onCodeLens(params: CodeLensParams): Promise {
+ await this.init()
+ return this.getProject(params.textDocument)?.onCodeLens(params) ?? null
+ }
+
async onDocumentLinks(params: DocumentLinkParams): Promise {
await this.init()
return this.getProject(params.textDocument)?.onDocumentLinks(params) ?? null
@@ -961,6 +971,9 @@ export class TW {
hoverProvider: true,
colorProvider: true,
codeActionProvider: true,
+ codeLensProvider: {
+ resolveProvider: false,
+ },
documentLinkProvider: {},
completionProvider: {
resolveProvider: true,
diff --git a/packages/tailwindcss-language-server/tests/code-lens/source-inline.test.ts b/packages/tailwindcss-language-server/tests/code-lens/source-inline.test.ts
new file mode 100644
index 00000000..55f6f22e
--- /dev/null
+++ b/packages/tailwindcss-language-server/tests/code-lens/source-inline.test.ts
@@ -0,0 +1,101 @@
+import { expect } from 'vitest'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
+
+defineTest({
+ name: 'Code lenses are displayed for @source inline(…)',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ features: ['source-inline'],
+ }),
+ }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'css',
+ text: css`
+ @import 'tailwindcss';
+ @source inline("{,{hover,focus}:}{flex,underline,bg-red-{50,{100..900.100},950}}");
+ `,
+ })
+
+ let lenses = await document.codeLenses()
+
+ expect(lenses).toEqual([
+ {
+ range: {
+ start: { line: 1, character: 15 },
+ end: { line: 1, character: 81 },
+ },
+ command: {
+ title: 'Generates 15 classes',
+ command: '',
+ },
+ },
+ ])
+ },
+})
+
+defineTest({
+ name: 'The user is warned when @source inline(…) generates a lerge amount of CSS',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ features: ['source-inline'],
+ }),
+ }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'css',
+ text: css`
+ @import 'tailwindcss';
+ @source inline("{,dark:}{,{sm,md,lg,xl,2xl}:}{,{hover,focus,active}:}{flex,underline,bg-red-{50,{100..900.100},950}{,/{0..100}}}");
+ `,
+ })
+
+ let lenses = await document.codeLenses()
+
+ expect(lenses).toEqual([
+ {
+ range: {
+ start: { line: 1, character: 15 },
+ end: { line: 1, character: 129 },
+ },
+ command: {
+ title: 'Generates 14,784 classes',
+ command: '',
+ },
+ },
+ {
+ range: {
+ start: { line: 1, character: 15 },
+ end: { line: 1, character: 129 },
+ },
+ command: {
+ title: 'At least 3MB of CSS',
+ command: '',
+ },
+ },
+ {
+ range: {
+ start: { line: 1, character: 15 },
+ end: { line: 1, character: 129 },
+ },
+ command: {
+ title: 'This may slow down your bundler/browser',
+ command: '',
+ },
+ },
+ ])
+ },
+})
diff --git a/packages/tailwindcss-language-server/tests/completions/at-config.test.js b/packages/tailwindcss-language-server/tests/completions/at-config.test.js
index 15d99ac6..60ee1495 100644
--- a/packages/tailwindcss-language-server/tests/completions/at-config.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/at-config.test.js
@@ -271,6 +271,51 @@ withFixture('v4/dependencies', (c) => {
})
})
+ test.concurrent('@source not', async ({ expect }) => {
+ let result = await completion({
+ text: '@source not "',
+ lang: 'css',
+ position: {
+ line: 0,
+ character: 13,
+ },
+ })
+
+ expect(result).toEqual({
+ isIncomplete: false,
+ items: [
+ {
+ label: 'index.html',
+ kind: 17,
+ data: expect.anything(),
+ textEdit: {
+ newText: 'index.html',
+ range: { start: { line: 0, character: 13 }, end: { line: 0, character: 13 } },
+ },
+ },
+ {
+ label: 'sub-dir/',
+ kind: 19,
+ command: { command: 'editor.action.triggerSuggest', title: '' },
+ data: expect.anything(),
+ textEdit: {
+ newText: 'sub-dir/',
+ range: { start: { line: 0, character: 13 }, end: { line: 0, character: 13 } },
+ },
+ },
+ {
+ label: 'tailwind.config.js',
+ kind: 17,
+ data: expect.anything(),
+ textEdit: {
+ newText: 'tailwind.config.js',
+ range: { start: { line: 0, character: 13 }, end: { line: 0, character: 13 } },
+ },
+ },
+ ],
+ })
+ })
+
test.concurrent('@source directory', async ({ expect }) => {
let result = await completion({
text: '@source "./sub-dir/',
@@ -297,6 +342,58 @@ withFixture('v4/dependencies', (c) => {
})
})
+ test.concurrent('@source not directory', async ({ expect }) => {
+ let result = await completion({
+ text: '@source not "./sub-dir/',
+ lang: 'css',
+ position: {
+ line: 0,
+ character: 23,
+ },
+ })
+
+ expect(result).toEqual({
+ isIncomplete: false,
+ items: [
+ {
+ label: 'colors.js',
+ kind: 17,
+ data: expect.anything(),
+ textEdit: {
+ newText: 'colors.js',
+ range: { start: { line: 0, character: 23 }, end: { line: 0, character: 23 } },
+ },
+ },
+ ],
+ })
+ })
+
+ test.concurrent('@source inline(…)', async ({ expect }) => {
+ let result = await completion({
+ text: '@source inline("',
+ lang: 'css',
+ position: {
+ line: 0,
+ character: 16,
+ },
+ })
+
+ expect(result).toEqual(null)
+ })
+
+ test.concurrent('@source not inline(…)', async ({ expect }) => {
+ let result = await completion({
+ text: '@source not inline("',
+ lang: 'css',
+ position: {
+ line: 0,
+ character: 20,
+ },
+ })
+
+ expect(result).toEqual(null)
+ })
+
test.concurrent('@import "…" source(…)', async ({ expect }) => {
let result = await completion({
text: '@import "tailwindcss" source("',
diff --git a/packages/tailwindcss-language-server/tests/document-links/document-links.test.js b/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
index 861f74c9..0fc76cb6 100644
--- a/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
+++ b/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
@@ -131,6 +131,44 @@ withFixture('v4/basic', (c) => {
],
})
+ testDocumentLinks('source not: file exists', {
+ text: '@source not "index.html";',
+ lang: 'css',
+ expected: [
+ {
+ target: `file://${path
+ .resolve('./tests/fixtures/v4/basic/index.html')
+ .replace(/@/g, '%40')}`,
+ range: { start: { line: 0, character: 12 }, end: { line: 0, character: 24 } },
+ },
+ ],
+ })
+
+ testDocumentLinks('source not: file does not exist', {
+ text: '@source not "does-not-exist.html";',
+ lang: 'css',
+ expected: [
+ {
+ target: `file://${path
+ .resolve('./tests/fixtures/v4/basic/does-not-exist.html')
+ .replace(/@/g, '%40')}`,
+ range: { start: { line: 0, character: 12 }, end: { line: 0, character: 33 } },
+ },
+ ],
+ })
+
+ testDocumentLinks('@source inline(…)', {
+ text: '@source inline("m-{1,2,3}");',
+ lang: 'css',
+ expected: [],
+ })
+
+ testDocumentLinks('@source not inline(…)', {
+ text: '@source not inline("m-{1,2,3}");',
+ lang: 'css',
+ expected: [],
+ })
+
testDocumentLinks('Directories in source(…) show links', {
text: `
@import "tailwindcss" source("../../");
diff --git a/packages/tailwindcss-language-server/tests/hover/hover.test.js b/packages/tailwindcss-language-server/tests/hover/hover.test.js
index 5c8bcb7f..ac97e414 100644
--- a/packages/tailwindcss-language-server/tests/hover/hover.test.js
+++ b/packages/tailwindcss-language-server/tests/hover/hover.test.js
@@ -293,6 +293,101 @@ withFixture('v4/basic', (c) => {
},
})
+ testHover('css @source not glob expansion', {
+ exact: true,
+ lang: 'css',
+ text: `@source not "../{app,components}/**/*.jsx"`,
+ position: { line: 0, character: 23 },
+ expected: {
+ contents: {
+ kind: 'markdown',
+ value: [
+ '**Expansion**',
+ '```plaintext',
+ '- ../app/**/*.jsx',
+ '- ../components/**/*.jsx',
+ '```',
+ ].join('\n'),
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 42 },
+ },
+ },
+ expectedRange: {
+ start: { line: 2, character: 9 },
+ end: { line: 2, character: 18 },
+ },
+ })
+
+ testHover('css @source inline glob expansion', {
+ exact: true,
+ lang: 'css',
+ text: `@source inline("{hover:,active:,}m-{1,2,3}")`,
+ position: { line: 0, character: 23 },
+ expected: {
+ contents: {
+ kind: 'markdown',
+ value: [
+ '**Expansion**',
+ '```plaintext',
+ '- hover:m-1',
+ '- hover:m-2',
+ '- hover:m-3',
+ '- active:m-1',
+ '- active:m-2',
+ '- active:m-3',
+ '- m-1',
+ '- m-2',
+ '- m-3',
+ '```',
+ ].join('\n'),
+ },
+ range: {
+ start: { line: 0, character: 15 },
+ end: { line: 0, character: 43 },
+ },
+ },
+ expectedRange: {
+ start: { line: 2, character: 9 },
+ end: { line: 2, character: 15 },
+ },
+ })
+
+ testHover('css @source not inline glob expansion', {
+ exact: true,
+ lang: 'css',
+ text: `@source not inline("{hover:,active:,}m-{1,2,3}")`,
+ position: { line: 0, character: 23 },
+ expected: {
+ contents: {
+ kind: 'markdown',
+ value: [
+ '**Expansion**',
+ '```plaintext',
+ '- hover:m-1',
+ '- hover:m-2',
+ '- hover:m-3',
+ '- active:m-1',
+ '- active:m-2',
+ '- active:m-3',
+ '- m-1',
+ '- m-2',
+ '- m-3',
+ '```',
+ ].join('\n'),
+ },
+ range: {
+ start: { line: 0, character: 19 },
+ end: { line: 0, character: 47 },
+ },
+ },
+ expectedRange: {
+ start: { line: 2, character: 9 },
+ end: { line: 2, character: 18 },
+ },
+ })
+
testHover('--theme() works inside @media queries', {
lang: 'tailwindcss',
text: `@media (width>=--theme(--breakpoint-xl)) { .foo { color: red; } }`,
diff --git a/packages/tailwindcss-language-server/tests/utils/client.ts b/packages/tailwindcss-language-server/tests/utils/client.ts
index d6d317bb..3a860230 100644
--- a/packages/tailwindcss-language-server/tests/utils/client.ts
+++ b/packages/tailwindcss-language-server/tests/utils/client.ts
@@ -1,6 +1,8 @@
import type { Settings } from '@tailwindcss/language-service/src/util/state'
import {
ClientCapabilities,
+ CodeLens,
+ CodeLensRequest,
CompletionList,
CompletionParams,
Diagnostic,
@@ -45,6 +47,7 @@ import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/
import { DefaultMap } from '../../src/util/default-map'
import { connect, ConnectOptions } from './connection'
import type { DeepPartial } from '@tailwindcss/language-service/src/types'
+import type { Feature } from '@tailwindcss/language-service/src/features'
export interface DocumentDescriptor {
/**
@@ -94,6 +97,11 @@ export interface ClientDocument {
*/
reopen(): Promise
+ /**
+ * Code lenses in the document
+ */
+ codeLenses(): Promise
+
/**
* The diagnostics for the current version of this document
*/
@@ -163,6 +171,14 @@ export interface ClientOptions extends ConnectOptions {
* Settings to provide the server immediately when it starts
*/
settings?: DeepPartial
+
+ /**
+ * Additional features to force-enable
+ *
+ * These should normally be enabled by the server based on the project
+ * and the Tailwind CSS version it detects
+ */
+ features?: Feature[]
}
export interface Client extends ClientWorkspace {
@@ -387,6 +403,7 @@ export async function createClient(opts: ClientOptions): Promise {
workspaceFolders,
initializationOptions: {
testMode: true,
+ additionalFeatures: opts.features,
...opts.options,
},
})
@@ -677,6 +694,14 @@ export async function createClientWorkspace({
return results
}
+ async function codeLenses() {
+ return await conn.sendRequest(CodeLensRequest.type, {
+ textDocument: {
+ uri: uri.toString(),
+ },
+ })
+ }
+
return {
uri,
reopen,
@@ -687,6 +712,7 @@ export async function createClientWorkspace({
symbols,
completions,
diagnostics,
+ codeLenses,
}
}
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index d5fddaaa..e73f6ca3 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -40,6 +40,7 @@
"vscode-languageserver-textdocument": "1.0.11"
},
"devDependencies": {
+ "@types/braces": "3.0.1",
"@types/css.escape": "^1.5.2",
"@types/dedent": "^0.7.2",
"@types/line-column": "^1.0.2",
diff --git a/packages/tailwindcss-language-service/src/codeLensProvider.ts b/packages/tailwindcss-language-service/src/codeLensProvider.ts
new file mode 100644
index 00000000..b00df983
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/codeLensProvider.ts
@@ -0,0 +1,78 @@
+import type { Range, TextDocument } from 'vscode-languageserver-textdocument'
+import type { State } from './util/state'
+import type { CodeLens } from 'vscode-languageserver'
+import braces from 'braces'
+import { findAll, indexToPosition } from './util/find'
+import { absoluteRange } from './util/absoluteRange'
+import { formatBytes } from './util/format-bytes'
+import { estimatedClassSize } from './util/estimated-class-size'
+
+export async function getCodeLens(state: State, doc: TextDocument): Promise {
+ if (!state.enabled) return []
+
+ let groups: CodeLens[][] = await Promise.all([
+ //
+ sourceInlineCodeLens(state, doc),
+ ])
+
+ return groups.flat()
+}
+
+const SOURCE_INLINE_PATTERN = /@source(?:\s+not)?\s*inline\((?'[^']+'|"[^"]+")/dg
+async function sourceInlineCodeLens(state: State, doc: TextDocument): Promise {
+ if (!state.features.includes('source-inline')) return []
+
+ let text = doc.getText()
+
+ let countFormatter = new Intl.NumberFormat('en', {
+ maximumFractionDigits: 2,
+ })
+
+ let lenses: CodeLens[] = []
+
+ for (let match of findAll(SOURCE_INLINE_PATTERN, text)) {
+ let glob = match.groups.glob.slice(1, -1)
+
+ // Perform brace expansion
+ let expanded = new Set(braces.expand(glob))
+ if (expanded.size < 2) continue
+
+ let slice: Range = absoluteRange({
+ start: indexToPosition(text, match.indices.groups.glob[0]),
+ end: indexToPosition(text, match.indices.groups.glob[1]),
+ })
+
+ let size = 0
+ for (let className of expanded) {
+ size += estimatedClassSize(className)
+ }
+
+ lenses.push({
+ range: slice,
+ command: {
+ title: `Generates ${countFormatter.format(expanded.size)} classes`,
+ command: '',
+ },
+ })
+
+ if (size >= 1_000_000) {
+ lenses.push({
+ range: slice,
+ command: {
+ title: `At least ${formatBytes(size)} of CSS`,
+ command: '',
+ },
+ })
+
+ lenses.push({
+ range: slice,
+ command: {
+ title: `This may slow down your bundler/browser`,
+ command: '',
+ },
+ })
+ }
+ }
+
+ return lenses
+}
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 1579fd93..3cdd511a 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -1866,6 +1866,19 @@ function provideCssDirectiveCompletions(
},
})
+ if (state.features.includes('source-not')) {
+ items.push({
+ label: '@source not',
+ documentation: {
+ kind: 'markdown' as typeof MarkupKind.Markdown,
+ value: `Use the \`@source not\` directive to ignore files when scanning.\n\n[Tailwind CSS Documentation](${docsUrl(
+ state.version,
+ 'functions-and-directives/#source',
+ )})`,
+ },
+ })
+ }
+
items.push({
label: '@plugin',
documentation: {
diff --git a/packages/tailwindcss-language-service/src/completions/file-paths.test.ts b/packages/tailwindcss-language-service/src/completions/file-paths.test.ts
index c79fcbdd..ed521392 100644
--- a/packages/tailwindcss-language-service/src/completions/file-paths.test.ts
+++ b/packages/tailwindcss-language-service/src/completions/file-paths.test.ts
@@ -15,6 +15,7 @@ test('Detecting v3 directives that point to files', async () => {
// The following are not supported in v3
await expect(find('@plugin "./')).resolves.toEqual(null)
await expect(find('@source "./')).resolves.toEqual(null)
+ await expect(find('@source not "./')).resolves.toEqual(null)
await expect(find('@import "tailwindcss" source("./')).resolves.toEqual(null)
await expect(find('@tailwind utilities source("./')).resolves.toEqual(null)
})
@@ -42,6 +43,12 @@ test('Detecting v4 directives that point to files', async () => {
suggest: 'source',
})
+ await expect(find('@source not "./')).resolves.toEqual({
+ directive: 'source',
+ partial: './',
+ suggest: 'source',
+ })
+
await expect(find('@import "tailwindcss" source("./')).resolves.toEqual({
directive: 'import',
partial: './',
@@ -54,3 +61,12 @@ test('Detecting v4 directives that point to files', async () => {
suggest: 'directory',
})
})
+
+test('@source inline is ignored', async () => {
+ function find(text: string) {
+ return findFileDirective({ enabled: true, v4: true }, text)
+ }
+
+ await expect(find('@source inline("')).resolves.toEqual(null)
+ await expect(find('@source not inline("')).resolves.toEqual(null)
+})
diff --git a/packages/tailwindcss-language-service/src/completions/file-paths.ts b/packages/tailwindcss-language-service/src/completions/file-paths.ts
index a99325be..acf4f9fa 100644
--- a/packages/tailwindcss-language-service/src/completions/file-paths.ts
+++ b/packages/tailwindcss-language-service/src/completions/file-paths.ts
@@ -1,7 +1,10 @@
import type { State } from '../util/state'
// @config, @plugin, @source
-const PATTERN_CUSTOM_V4 = /@(?config|plugin|source)\s*(?'[^']*|"[^"]*)$/
+// - @source inline("…") is *not* a file directive
+// - @source not inline("…") is *not* a file directive
+const PATTERN_CUSTOM_V4 =
+ /@(?config|plugin|source)(?\s+not)?\s*(?'[^']*|"[^"]*)$/
const PATTERN_CUSTOM_V3 = /@(?config)\s*(?'[^']*|"[^"]*)$/
// @import … source('…')
@@ -26,6 +29,7 @@ export async function findFileDirective(state: State, text: string): Promise 0
let directive = match.groups.directive
let partial = match.groups.partial?.slice(1) ?? '' // remove leading quote
@@ -40,6 +44,7 @@ export async function findFileDirective(state: State, text: string): Promise 0
+ if (isNot) return null
+
let directive = match.groups.directive
let partial = match.groups.partial.slice(1) // remove leading quote
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidSourceDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidSourceDiagnostics.ts
index 2ac52a08..24fdcd9c 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidSourceDiagnostics.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidSourceDiagnostics.ts
@@ -14,7 +14,7 @@ const PATTERN_UTIL_SOURCE =
// @source …
const PATTERN_AT_SOURCE =
- /(?:\s|^)@(?source)\s*(?'[^']*'?|"[^"]*"?|[a-z]*|\)|;)/dg
+ /(?:\s|^)@(?source)\s*(?not)?\s*(?'[^']*'?|"[^"]*"?|[a-z]*(?:\([^)]+\))?|\)|;)/dg
const HAS_DRIVE_LETTER = /^[A-Z]:/
@@ -135,6 +135,11 @@ export function getInvalidSourceDiagnostics(
})
}
+ // `@source inline(…)` is fine
+ else if (directive === 'source' && source.startsWith('inline(')) {
+ //
+ }
+
// - `@import "tailwindcss" source(no)`
// - `@tailwind utilities source('')`
else if (directive === 'source' && source !== 'none' && !isQuoted) {
diff --git a/packages/tailwindcss-language-service/src/documentLinksProvider.ts b/packages/tailwindcss-language-service/src/documentLinksProvider.ts
index 76e39ed7..ea2e7bd9 100644
--- a/packages/tailwindcss-language-service/src/documentLinksProvider.ts
+++ b/packages/tailwindcss-language-service/src/documentLinksProvider.ts
@@ -18,7 +18,7 @@ export function getDocumentLinks(
if (state.v4) {
patterns.push(
/@plugin\s*(?'[^']+'|"[^"]+")/g,
- /@source\s*(?'[^']+'|"[^"]+")/g,
+ /@source(?:\s+not)?\s*(?'[^']+'|"[^"]+")/g,
/@import\s*('[^']*'|"[^"]*")\s*(layer\([^)]+\)\s*)?source\((?'[^']*'?|"[^"]*"?)/g,
/@reference\s*('[^']*'|"[^"]*")\s*source\((?'[^']*'?|"[^"]*"?)/g,
/@tailwind\s*utilities\s*source\((?'[^']*'?|"[^"]*"?)/g,
diff --git a/packages/tailwindcss-language-service/src/features.ts b/packages/tailwindcss-language-service/src/features.ts
index 60181bf7..6aa9236e 100644
--- a/packages/tailwindcss-language-service/src/features.ts
+++ b/packages/tailwindcss-language-service/src/features.ts
@@ -15,6 +15,8 @@ export type Feature =
| 'jit'
| 'separator:root'
| 'separator:options'
+ | 'source-not'
+ | 'source-inline'
/**
* Determine a list of features that are supported by the given Tailwind CSS version
@@ -39,15 +41,21 @@ export function supportedFeatures(version: string, mod?: unknown): Feature[] {
}
if (isInsidersV4) {
- return ['css-at-theme', 'layer:base', 'content-list']
+ return ['css-at-theme', 'layer:base', 'content-list', 'source-inline', 'source-not']
}
- if (!isInsidersV3 && semver.gte(version, '4.0.0-alpha.1')) {
- return ['css-at-theme', 'layer:base', 'content-list']
- }
+ if (!isInsidersV3) {
+ if (semver.gte(version, '4.1.0')) {
+ return ['css-at-theme', 'layer:base', 'content-list', 'source-inline', 'source-not']
+ }
- if (!isInsidersV3 && version.startsWith('0.0.0-oxide')) {
- return ['css-at-theme', 'layer:base', 'content-list']
+ if (semver.gte(version, '4.0.0-alpha.1')) {
+ return ['css-at-theme', 'layer:base', 'content-list']
+ }
+
+ if (version.startsWith('0.0.0-oxide')) {
+ return ['css-at-theme', 'layer:base', 'content-list']
+ }
}
if (semver.gte(version, '0.99.0')) {
diff --git a/packages/tailwindcss-language-service/src/hoverProvider.ts b/packages/tailwindcss-language-service/src/hoverProvider.ts
index eb5a7d1c..1db68bed 100644
--- a/packages/tailwindcss-language-service/src/hoverProvider.ts
+++ b/packages/tailwindcss-language-service/src/hoverProvider.ts
@@ -168,19 +168,24 @@ async function provideSourceGlobHover(
let text = getTextWithoutComments(document, 'css', range)
- let pattern = /@source\s*(?'[^']+'|"[^"]+")/dg
+ let patterns = [
+ /@source(?:\s+not)?\s*(?'[^']+'|"[^"]+")/dg,
+ /@source(?:\s+not)?\s*inline\((?'[^']+'|"[^"]+")/dg,
+ ]
- for (let match of findAll(pattern, text)) {
- let path = match.groups.path.slice(1, -1)
+ let matches = patterns.flatMap((pattern) => findAll(pattern, text))
- // Ignore paths that don't need brace expansion
- if (!path.includes('{') || !path.includes('}')) continue
+ for (let match of matches) {
+ let glob = match.groups.glob.slice(1, -1)
+
+ // Ignore globs that don't need brace expansion
+ if (!glob.includes('{') || !glob.includes('}')) continue
- // Ignore paths that don't contain the current position
+ // Ignore glob that don't contain the current position
let slice: Range = absoluteRange(
{
- start: indexToPosition(text, match.indices.groups.path[0]),
- end: indexToPosition(text, match.indices.groups.path[1]),
+ start: indexToPosition(text, match.indices.groups.glob[0]),
+ end: indexToPosition(text, match.indices.groups.glob[1]),
},
range,
)
@@ -188,8 +193,8 @@ async function provideSourceGlobHover(
if (!isWithinRange(position, slice)) continue
// Perform brace expansion
- let paths = new Set(braces.expand(path))
- if (paths.size < 2) continue
+ let expanded = new Set(braces.expand(glob))
+ if (expanded.size < 2) continue
return {
range: slice,
@@ -197,7 +202,7 @@ async function provideSourceGlobHover(
//
'**Expansion**',
'```plaintext',
- ...Array.from(paths, (path) => `- ${path}`),
+ ...Array.from(expanded, (entry) => `- ${entry}`),
'```',
]),
}
diff --git a/packages/tailwindcss-language-service/src/util/estimated-class-size.ts b/packages/tailwindcss-language-service/src/util/estimated-class-size.ts
new file mode 100644
index 00000000..57dc4235
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/util/estimated-class-size.ts
@@ -0,0 +1,35 @@
+import { segment } from './segment'
+
+/**
+ * Calculates the approximate size of a generated class
+ *
+ * This is meant to be a lower bound, as the actual size of a class can vary
+ * depending on the actual CSS properties and values, configured theme, etc…
+ */
+export function estimatedClassSize(className: string) {
+ let size = 0
+
+ // We estimate the size using the following structure which gives a reasonable
+ // lower bound on the size of the generated CSS:
+ //
+ // .class-name {
+ // &:variant-1 {
+ // &:variant-2 {
+ // …
+ // }
+ // }
+ // }
+
+ // Class name
+ size += 1 + className.length + 3
+ size += 2
+
+ // Variants + nesting
+ for (let [depth, variantName] of segment(className, ':').entries()) {
+ size += (depth + 1) * 2 + 2 + variantName.length + 3
+ size += (depth + 1) * 2 + 2
+ }
+
+ // ~1.95x is a rough growth factor due to the actual properties being present
+ return size * 1.95
+}
diff --git a/packages/tailwindcss-language-service/src/util/format-bytes.ts b/packages/tailwindcss-language-service/src/util/format-bytes.ts
new file mode 100644
index 00000000..a7b0b050
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/util/format-bytes.ts
@@ -0,0 +1,11 @@
+const UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte']
+
+export function formatBytes(n: number) {
+ let i = n == 0 ? 0 : Math.floor(Math.log(n) / Math.log(1000))
+ return new Intl.NumberFormat('en', {
+ notation: 'compact',
+ style: 'unit',
+ unit: UNITS[i],
+ unitDisplay: 'narrow',
+ }).format(n / 1000 ** i)
+}
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 3bdb1bc4..119e5f59 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -4,6 +4,7 @@ import type { Postcss } from 'postcss'
import type { KeywordColor } from './color'
import type * as culori from 'culori'
import type { DesignSystem } from './v4'
+import type { Feature } from '../features'
export type ClassNamesTree = {
[key: string]: ClassNamesTree
@@ -49,6 +50,7 @@ export type TailwindCssSettings = {
classFunctions: string[]
suggestions: boolean
hovers: boolean
+ codeLens: boolean
codeActions: boolean
validate: boolean
showPixelEquivalents: boolean
@@ -141,6 +143,7 @@ export interface State {
classListContainsMetadata?: boolean
pluginVersions?: string
completionItemData?: Record
+ features: Feature[]
// postcssPlugins?: { before: any[]; after: any[] }
}
@@ -185,6 +188,7 @@ export function getDefaultTailwindSettings(): Settings {
classAttributes: ['class', 'className', 'ngClass', 'class:list'],
classFunctions: [],
codeActions: true,
+ codeLens: true,
hovers: true,
suggestions: true,
validate: true,
@@ -221,6 +225,7 @@ export function createState(
): State {
return {
enabled: true,
+ features: [],
...partial,
editor: {
get connection(): Connection {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 7b9e68fa..faf20065 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,8 @@
- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258))
- v4: Make sure completions show after variants using arbitrary and bare values ([#1263](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1263))
+- v4: Add support for upcoming `@source not` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
+- v4: Add support for upcoming `@source inline(…)` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
# 0.14.9
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 036ca936..876a661b 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -210,6 +210,12 @@
"markdownDescription": "Enable code actions.",
"scope": "language-overridable"
},
+ "tailwindCSS.codeLens": {
+ "type": "boolean",
+ "default": true,
+ "markdownDescription": "Enable code lens.",
+ "scope": "language-overridable"
+ },
"tailwindCSS.colorDecorators": {
"type": "boolean",
"default": true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2378a376..67d58759 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -56,6 +56,9 @@ importers:
'@tailwindcss/typography':
specifier: 0.5.7
version: 0.5.7(tailwindcss@3.4.17)
+ '@types/braces':
+ specifier: 3.0.1
+ version: 3.0.1
'@types/color-name':
specifier: ^1.1.3
version: 1.1.4
@@ -297,6 +300,9 @@ importers:
specifier: 1.0.11
version: 1.0.11
devDependencies:
+ '@types/braces':
+ specifier: 3.0.1
+ version: 3.0.1
'@types/css.escape':
specifier: ^1.5.2
version: 1.5.2
From 182600d55606e81ea600e57be15580cf32b7fe73 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 19 Mar 2025 10:47:17 -0400
Subject: [PATCH 018/108] Ensure `classFunctions` completions work inside
`
+ `,
+ })
+
+ // let classes = clsx('')
+ // ^
+ let completion = await document.completions({ line: 1, character: 22 })
+
+ expect(completion?.items.length).toBe(12289)
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 3cdd511a..62c54594 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -22,7 +22,7 @@ import isObject from './util/isObject'
import { braceLevel, parenLevel } from './util/braceLevel'
import * as emmetHelper from 'vscode-emmet-helper-bundled'
import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation'
-import { isJsDoc, isJsxContext } from './util/js'
+import { isJsContext, isJsDoc, isJsxContext } from './util/js'
import { naturalExpand } from './util/naturalExpand'
import * as semver from './util/semver'
import { getTextWithoutComments } from './util/doc'
@@ -986,7 +986,11 @@ async function provideClassNameCompletions(
return provideAtApplyCompletions(state, document, position, context)
}
- if (isHtmlContext(state, document, position) || isJsxContext(state, document, position)) {
+ if (
+ isHtmlContext(state, document, position) ||
+ isJsContext(state, document, position) ||
+ isJsxContext(state, document, position)
+ ) {
return provideClassAttributeCompletions(state, document, position, context)
}
diff --git a/packages/tailwindcss-language-service/src/util/js.ts b/packages/tailwindcss-language-service/src/util/js.ts
index 1197d1e6..0c405a8d 100644
--- a/packages/tailwindcss-language-service/src/util/js.ts
+++ b/packages/tailwindcss-language-service/src/util/js.ts
@@ -12,6 +12,19 @@ export function isJsDoc(state: State, doc: TextDocument): boolean {
return [...jsLanguages, ...userJsLanguages].indexOf(doc.languageId) !== -1
}
+export function isJsContext(state: State, doc: TextDocument, position: Position): boolean {
+ let str = doc.getText({
+ start: { line: 0, character: 0 },
+ end: position,
+ })
+
+ let boundaries = getLanguageBoundaries(state, doc, str)
+
+ return boundaries
+ ? ['js', 'ts', 'jsx', 'tsx'].includes(boundaries[boundaries.length - 1].type)
+ : false
+}
+
export function isJsxContext(state: State, doc: TextDocument, position: Position): boolean {
let str = doc.getText({
start: { line: 0, character: 0 },
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index faf20065..468cdd08 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258))
+- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258), [#1272](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1272))
- v4: Make sure completions show after variants using arbitrary and bare values ([#1263](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1263))
- v4: Add support for upcoming `@source not` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
- v4: Add support for upcoming `@source inline(…)` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
From 2f41b839a3856efd1ebe10b656e7b4704cbd57d8 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 19 Mar 2025 11:48:21 -0400
Subject: [PATCH 019/108] LSP: Refresh internal caches when settings are
updated (#1273)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We use the pull model (`workspace/configuration`) to get settings for a
document and we cache these internally so we don't repeat these calls
multiple times for a given request. We're set up to listen for
configuration refresh notifications and update the project settings when
we get them.
Unfortunately, we didn't actually _register_ for these notifications, so
we never got them. This meant that if you changed the settings for an
already opened file or workspace folder, the language server would not
react to these changes. This PR fixes this by registering for
configuration change notifications and now open files with color
decorators, completions, etc… should react to changes in the settings as
needed.
If settings are updated and our langauge server doesn't react to or
handle these changes, it is definitely a bug. Hopefully this will squash
all of those particular ones but… we'll see. 😅
---
packages/tailwindcss-language-server/src/projects.ts | 8 +++-----
packages/tailwindcss-language-server/src/tw.ts | 6 ++++++
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
3 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index afc4515f..a8012920 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -1181,11 +1181,9 @@ export async function createProjectService(
if (state.enabled) {
refreshDiagnostics()
}
- if (settings.editor?.colorDecorators) {
- updateCapabilities()
- } else {
- connection.sendNotification('@/tailwindCSS/clearColors')
- }
+
+ updateCapabilities()
+ connection.sendNotification('@/tailwindCSS/clearColors')
},
onFileEvents,
async onHover(params: TextDocumentPositionParams): Promise {
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index fc6a87a8..48d7b5f7 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -33,6 +33,7 @@ import {
DocumentLinkRequest,
TextDocumentSyncKind,
CodeLensRequest,
+ DidChangeConfigurationNotification,
} from 'vscode-languageserver/node'
import { URI } from 'vscode-uri'
import normalizePath from 'normalize-path'
@@ -799,6 +800,7 @@ export class TW {
private updateCapabilities() {
if (!supportsDynamicRegistration(this.initializeParams)) {
+ this.connection.client.register(DidChangeConfigurationNotification.type, undefined)
return
}
@@ -810,12 +812,16 @@ export class TW {
let capabilities = BulkRegistration.create()
+ // TODO: We should *not* be re-registering these capabilities
+ // IDEA: These should probably be static registrations up front
capabilities.add(HoverRequest.type, { documentSelector: null })
capabilities.add(DocumentColorRequest.type, { documentSelector: null })
capabilities.add(CodeActionRequest.type, { documentSelector: null })
capabilities.add(CodeLensRequest.type, { documentSelector: null })
capabilities.add(DocumentLinkRequest.type, { documentSelector: null })
+ capabilities.add(DidChangeConfigurationNotification.type, undefined)
+ // TODO: Only re-register this if trigger characters change
capabilities.add(CompletionRequest.type, {
documentSelector: null,
resolveProvider: true,
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 468cdd08..fa2d07c3 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -6,6 +6,7 @@
- v4: Make sure completions show after variants using arbitrary and bare values ([#1263](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1263))
- v4: Add support for upcoming `@source not` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
- v4: Add support for upcoming `@source inline(…)` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
+- LSP: Refresh internal caches when settings are updated ([#1273](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1273))
# 0.14.9
From 747884f34b31c797db0915d1719276eae35431f1 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 12:37:21 -0400
Subject: [PATCH 020/108] Ignore regex literals when analyzing language
boundaries (#1275)
Fixes #929
---
.../tests/env/v4.test.js | 48 +++++
.../src/util/doc.ts | 150 ++++++++++++++
.../src/util/find.test.ts | 61 +-----
.../src/util/language-boundaries.test.ts | 191 ++++++++++++++++++
.../src/util/test-utils.ts | 65 ++++++
5 files changed, 455 insertions(+), 60 deletions(-)
create mode 100644 packages/tailwindcss-language-service/src/util/language-boundaries.test.ts
create mode 100644 packages/tailwindcss-language-service/src/util/test-utils.ts
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 76de7f15..3a7780af 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -791,3 +791,51 @@ defineTest({
})
},
})
+
+defineTest({
+ options: { only: true },
+ name: 'regex literals do not break language boundaries',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'javascriptreact',
+ text: js`
+ export default function Page() {
+ let styles = "str".match(/
+
+
+ `,
+ })
+
+ let boundaries = getLanguageBoundaries(file.state, file.doc)
+
+ expect(boundaries).toEqual([
+ {
+ type: 'html',
+ range: {
+ start: { line: 0, character: 0 },
+ end: { line: 1, character: 2 },
+ },
+ },
+ {
+ type: 'css',
+ range: {
+ start: { line: 1, character: 2 },
+ end: { line: 5, character: 2 },
+ },
+ },
+ {
+ type: 'html',
+ range: {
+ start: { line: 5, character: 2 },
+ end: { line: 7, character: 6 },
+ },
+ },
+ ])
+})
+
+test('script tags in HTML are treated as a separate boundary', ({ expect }) => {
+ let file = createDocument({
+ name: 'file.html',
+ lang: 'html',
+ content: html`
+
+ `,
+ })
+
+ let boundaries = getLanguageBoundaries(file.state, file.doc)
+
+ expect(boundaries).toEqual([
+ {
+ type: 'html',
+ range: {
+ start: { line: 0, character: 0 },
+ end: { line: 1, character: 2 },
+ },
+ },
+ {
+ type: 'js',
+ range: {
+ start: { line: 1, character: 2 },
+ end: { line: 5, character: 2 },
+ },
+ },
+ {
+ type: 'html',
+ range: {
+ start: { line: 5, character: 2 },
+ end: { line: 7, character: 6 },
+ },
+ },
+ ])
+})
+
+test('Vue files detect
,
+
+
+
+
+
+ Some documentation
+
+ `,
+ })
+
+ let boundaries = getLanguageBoundaries(file.state, file.doc)
+
+ expect(boundaries).toEqual([
+ {
+ type: 'none',
+ range: {
+ start: { line: 0, character: 0 },
+ end: { line: 0, character: 0 },
+ },
+ },
+ {
+ type: 'js',
+ range: {
+ start: { line: 0, character: 0 },
+ end: { line: 2, character: 0 },
+ },
+ },
+ {
+ type: 'none',
+ range: {
+ start: { line: 2, character: 0 },
+ end: { line: 3, character: 0 },
+ },
+ },
+ {
+ type: 'html',
+ range: {
+ start: { line: 3, character: 0 },
+ end: { line: 5, character: 0 },
+ },
+ },
+ {
+ type: 'none',
+ range: {
+ start: { line: 5, character: 0 },
+ end: { line: 6, character: 0 },
+ },
+ },
+ {
+ type: 'css',
+ range: {
+ start: { line: 6, character: 0 },
+ end: { line: 10, character: 0 },
+ },
+ },
+ {
+ type: 'none',
+ range: {
+ start: { line: 10, character: 0 },
+ end: { line: 13, character: 16 },
+ },
+ },
+ ])
+})
diff --git a/packages/tailwindcss-language-service/src/util/test-utils.ts b/packages/tailwindcss-language-service/src/util/test-utils.ts
new file mode 100644
index 00000000..088dade5
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/util/test-utils.ts
@@ -0,0 +1,65 @@
+import { createState, getDefaultTailwindSettings, Settings } from './state'
+import { TextDocument } from 'vscode-languageserver-textdocument'
+import type { DeepPartial } from '../types'
+import dedent from 'dedent'
+
+export const js = dedent
+export const jsx = dedent
+export const ts = dedent
+export const tsx = dedent
+export const css = dedent
+export const html = dedent
+
+export function createDocument({
+ name,
+ lang,
+ content,
+ settings,
+}: {
+ name: string
+ lang: string
+ content: string | string[]
+ settings?: DeepPartial
+}) {
+ let doc = TextDocument.create(
+ `file://${name}`,
+ lang,
+ 1,
+ typeof content === 'string' ? content : content.join('\n'),
+ )
+ let defaults = getDefaultTailwindSettings()
+ settings ??= {}
+ let state = createState({
+ editor: {
+ getConfiguration: async () => ({
+ ...defaults,
+ ...settings,
+ tailwindCSS: {
+ ...defaults.tailwindCSS,
+ ...settings.tailwindCSS,
+ lint: {
+ ...defaults.tailwindCSS.lint,
+ ...(settings.tailwindCSS?.lint ?? {}),
+ },
+ experimental: {
+ ...defaults.tailwindCSS.experimental,
+ ...(settings.tailwindCSS?.experimental ?? {}),
+ },
+ files: {
+ ...defaults.tailwindCSS.files,
+ ...(settings.tailwindCSS?.files ?? {}),
+ },
+ },
+ editor: {
+ ...defaults.editor,
+ ...settings.editor,
+ },
+ }),
+ },
+ })
+
+ return {
+ doc,
+ state,
+ }
+}
From f91a3bceb7a720669af3e5ab2db4e8e0f659d3cc Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 12:43:27 -0400
Subject: [PATCH 021/108] Improve error message when a workspace folder is
inaccessible (#1276)
On Linux when using a LSP client that does not support watching files on
behalf of the server, we'll use Parcel Watcher (if possible). If we
start the watcher with a non-existent or inaccessible directory, it will
throw an error with a very unhelpful message: "Bad file descriptor".
The best thing we can do is an initial check for access to the directory
and log a more helpful error message if it fails:
See
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/884#issuecomment-1927660975
---------
Co-authored-by: Robin Malfait
---
.../tailwindcss-language-server/src/tw.ts | 22 ++++++++++++++++++-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index 48d7b5f7..d5cfba20 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -38,6 +38,7 @@ import {
import { URI } from 'vscode-uri'
import normalizePath from 'normalize-path'
import * as path from 'node:path'
+import * as fs from 'node:fs/promises'
import type * as chokidar from 'chokidar'
import picomatch from 'picomatch'
import * as parcel from './watcher/index.js'
@@ -174,6 +175,26 @@ export class TW {
}
private async _initFolder(baseUri: URI): Promise {
+ // NOTE: We do this check because on Linux when using an LSP client that does
+ // not support watching files on behalf of the server, we'll use Parcel
+ // Watcher (if possible). If we start the watcher with a non-existent or
+ // inaccessible directory, it will throw an error with a very unhelpful
+ // message: "Bad file descriptor"
+ //
+ // The best thing we can do is an initial check for access to the directory
+ // and log a more helpful error message if it fails.
+ let base = baseUri.fsPath
+
+ try {
+ await fs.access(base, fs.constants.F_OK | fs.constants.R_OK)
+ } catch (err) {
+ console.error(
+ `Unable to access the workspace folder [${base}]. This may happen if the directory does not exist or the current user does not have the necessary permissions to access it.`,
+ )
+ console.error(err)
+ return
+ }
+
let initUserLanguages = this.initializeParams.initializationOptions?.userLanguages ?? {}
if (Object.keys(initUserLanguages).length > 0) {
@@ -182,7 +203,6 @@ export class TW {
)
}
- let base = baseUri.fsPath
let workspaceFolders: Array = []
let globalSettings = await this.settingsCache.get()
let ignore = globalSettings.tailwindCSS.files.exclude
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index fa2d07c3..32733ec8 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -7,6 +7,7 @@
- v4: Add support for upcoming `@source not` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
- v4: Add support for upcoming `@source inline(…)` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
- LSP: Refresh internal caches when settings are updated ([#1273](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1273))
+- LSP: Improve error message when a workspace folder does not exist or is inaccesible to the current user ([#1276](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1276))
# 0.14.9
From 741925942f35c3c220527bcc58fb4d2d025da3f7 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 15:09:57 -0400
Subject: [PATCH 022/108] =?UTF-8?q?v4:=20Show=20theme=20key=20completions?=
=?UTF-8?q?=20in=20`var(=E2=80=A6)`=20(#1274)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Closes #1209
---
.../tests/completions/completions.test.js | 51 +++++++++++++++++
.../src/completionProvider.ts | 56 +++++++++++++++----
.../src/util/rewriting/lookup.ts | 2 +-
.../src/util/v4/design-system.ts | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
5 files changed, 99 insertions(+), 13 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index 15c220bb..024213cb 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -2,6 +2,7 @@ import { test, expect } from 'vitest'
import { withFixture } from '../common'
import { css, defineTest, html, js } from '../../src/testing'
import { createClient } from '../utils/client'
+import { CompletionItemKind } from 'vscode-languageserver'
function buildCompletion(c) {
return async function completion({
@@ -798,3 +799,53 @@ defineTest({
expect(completion?.items.length).toBe(12289)
},
})
+
+defineTest({
+ name: 'v4: Theme key completions show in var(…)',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+
+ @theme {
+ --color-custom: #000;
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ settings: {
+ tailwindCSS: {
+ classFunctions: ['clsx'],
+ },
+ },
+ lang: 'css',
+ text: css`
+ .foo {
+ color: var();
+ }
+ `,
+ })
+
+ // color: var();
+ // ^
+ let completion = await document.completions({ line: 1, character: 13 })
+
+ expect(completion).toEqual({
+ isIncomplete: false,
+ items: expect.arrayContaining([
+ // From the default theme
+ expect.objectContaining({ label: '--font-sans' }),
+
+ // From the `@theme` block in the CSS file
+ expect.objectContaining({
+ label: '--color-custom',
+
+ // And it's shown as a color
+ kind: CompletionItemKind.Color,
+ documentation: '#000000',
+ }),
+ ]),
+ })
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 62c54594..978f118c 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -1014,7 +1014,7 @@ function provideCssHelperCompletions(
const match = text
.substr(0, text.length - 1) // don't include that extra character from earlier
- .match(/[\s:;/*(){}](?config|theme|--theme)\(\s*['"]?(?[^)'"]*)$/)
+ .match(/[\s:;/*(){}](?config|theme|--theme|var)\(\s*['"]?(?[^)'"]*)$/d)
if (match === null) {
return null
@@ -1040,17 +1040,15 @@ function provideCssHelperCompletions(
end: position,
}
- if (state.v4 && match.groups.helper === '--theme') {
- // List known theme keys
- let validThemeKeys = resolveKnownThemeKeys(state.designSystem)
+ if (
+ state.v4 &&
+ (match.groups.helper === '--theme' ||
+ match.groups.helper === 'theme' ||
+ match.groups.helper === 'var')
+ ) {
+ let items: CompletionItem[] = themeKeyCompletions(state)
- let items: CompletionItem[] = validThemeKeys.map((themeKey, index) => {
- return {
- label: themeKey,
- sortText: naturalExpand(index, validThemeKeys.length),
- kind: 9,
- }
- })
+ editRange.start.character = match.indices.groups.helper[1] + 1
return withDefaults(
{ isIncomplete: false, items },
@@ -1065,6 +1063,8 @@ function provideCssHelperCompletions(
)
}
+ if (match.groups.helper === 'var') return null
+
let base = match.groups.helper === 'config' ? state.config : dlv(state.config, 'theme', {})
let parts = path.split(/([\[\].]+)/)
let keys = parts.filter((_, i) => i % 2 === 0)
@@ -2486,3 +2486,37 @@ async function knownUtilityFunctionArguments(state: State, fn: UtilityFn): Promi
return args
}
+
+export function themeKeyCompletions(state: State): CompletionItem[] {
+ if (!state.v4) return null
+ if (!state.designSystem) return null
+
+ let knownThemeKeys = resolveKnownThemeKeys(state.designSystem)
+
+ return knownThemeKeys.map((themeKey, index) => {
+ let value = state.designSystem.resolveThemeValue(themeKey, true)
+ let documentation: string | undefined
+
+ let color = getColorFromValue(value)
+ if (color !== null) {
+ if (typeof color !== 'string' && (color.alpha ?? 1) !== 0) {
+ documentation = formatColor(color)
+ }
+
+ return {
+ label: themeKey,
+ kind: CompletionItemKind.Color,
+ sortText: naturalExpand(index, knownThemeKeys.length),
+ detail: value,
+ documentation,
+ }
+ }
+
+ return {
+ label: themeKey,
+ kind: CompletionItemKind.Variable,
+ sortText: naturalExpand(index, knownThemeKeys.length),
+ detail: value,
+ }
+ })
+}
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/lookup.ts b/packages/tailwindcss-language-service/src/util/rewriting/lookup.ts
index ae8e70e7..2dbb7146 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/lookup.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/lookup.ts
@@ -8,5 +8,5 @@ export function resolveVariableValue(design: DesignSystem, name: string) {
name = `--${name.slice(prefix.length + 3)}`
}
- return design.resolveThemeValue?.(name) ?? null
+ return design.resolveThemeValue?.(name, true) ?? null
}
diff --git a/packages/tailwindcss-language-service/src/util/v4/design-system.ts b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
index 3fb3c401..20b7ff32 100644
--- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
@@ -39,7 +39,7 @@ export interface DesignSystem {
getVariants(): VariantEntry[]
// Optional because it did not exist in earlier v4 alpha versions
- resolveThemeValue?(path: string): string | undefined
+ resolveThemeValue?(path: string, forceInline?: boolean): string | undefined
}
export interface DesignSystem {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 32733ec8..7b4d62bb 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -8,6 +8,7 @@
- v4: Add support for upcoming `@source inline(…)` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
- LSP: Refresh internal caches when settings are updated ([#1273](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1273))
- LSP: Improve error message when a workspace folder does not exist or is inaccesible to the current user ([#1276](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1276))
+- v4: Show theme key completions in `var(…)` in CSS ([#1274](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1274))
# 0.14.9
From fd34756a9642e4969ca07dde9680bef0184961d0 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 14:29:24 -0400
Subject: [PATCH 023/108] 0.14.10
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index fa0a7e91..f04ee81c 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.9",
+ "version": "0.14.10",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index e73f6ca3..6082fc86 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.9",
+ "version": "0.14.10",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 7b4d62bb..1e8a1970 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.10
+
- Detect classes in JS/TS functions and tagged template literals with the `tailwindCSS.classFunctions` setting ([#1258](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1258), [#1272](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1272))
- v4: Make sure completions show after variants using arbitrary and bare values ([#1263](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1263))
- v4: Add support for upcoming `@source not` feature ([#1262](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1262))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 876a661b..2747fd44 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.9",
+ "version": "0.14.10",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 43f90004a6b3bc785221c17d84923e020875efc4 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 15:20:52 -0400
Subject: [PATCH 024/108] Remove `only: true`
oops
---
packages/tailwindcss-language-server/tests/env/v4.test.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 3a7780af..36639f55 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -793,7 +793,6 @@ defineTest({
})
defineTest({
- options: { only: true },
name: 'regex literals do not break language boundaries',
fs: {
'app.css': css`
From 20890ea2ce0ed37436b457c9e6eb76ef340ea251 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 18:52:04 -0400
Subject: [PATCH 025/108] Discard function matches that appear after the
current cursor in completions (#1278)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Look, it's not a fancy shiny new feature without a bug 😅
When we discover function matches via `tailwindCSS.classFunctions` we
were implicitly breaking some assumptions used by the completions code:
- No matches for class attributes or functions that appear _after_ the
cursor position are found (we weren't discarding these function matches
but we need to)
- The *last* match is always the one that corresponds to the current
cursor position (we collect function matches separately so the order was
wrong)
This PR fixes this by doing two things:
- Discarding function matches that appear after the current cursor
position
- Sorting the list of attribute and function matches so the last one in
the list is always the latest one in the document (up to the cursor)
---
.../tests/completions/completions.test.js | 58 +++++++++++++++++++
.../src/completionProvider.ts | 13 +++--
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
3 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index 024213cb..96f8188a 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -849,3 +849,61 @@ defineTest({
})
},
})
+
+defineTest({
+ name: 'v4: class function completions mixed with class attribute completions work',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['className'],
+ classFunctions: ['cn', 'cva'],
+ },
+ },
+ lang: 'javascriptreact',
+ text: js`
+ let x = cva("")
+
+ export function Button() {
+ return
+ }
+
+ export function Button2() {
+ return
+ }
+
+ let y = cva("")
+ `,
+ })
+
+ // let x = cva("");
+ // ^
+ let completionA = await document.completions({ line: 0, character: 13 })
+
+ expect(completionA?.items.length).toBe(12289)
+
+ // return ;
+ // ^
+ let completionB = await document.completions({ line: 3, character: 30 })
+
+ expect(completionB?.items.length).toBe(12289)
+
+ // return ;
+ // ^
+ let completionC = await document.completions({ line: 7, character: 30 })
+
+ expect(completionC?.items.length).toBe(12289)
+
+ // let y = cva("");
+ // ^
+ let completionD = await document.completions({ line: 10, character: 13 })
+
+ expect(completionD?.items.length).toBe(12289)
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 978f118c..5c0c5552 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -703,8 +703,9 @@ async function provideClassAttributeCompletions(
position: Position,
context?: CompletionContext,
): Promise {
+ let current = document.offsetAt(position)
let range: Range = {
- start: document.positionAt(Math.max(0, document.offsetAt(position) - SEARCH_RANGE)),
+ start: document.positionAt(Math.max(0, current - SEARCH_RANGE)),
end: position,
}
@@ -734,13 +735,17 @@ async function provideClassAttributeCompletions(
let offset = document.offsetAt(boundary.range.start)
let fnMatches = matchClassFunctions(str, settings.classFunctions)
- fnMatches.forEach((match) => {
+ for (let match of fnMatches) {
if (match.index) match.index += offset
- })
+ if (match.index > current) continue
- matches.push(...fnMatches)
+ matches.push(match)
+ }
}
+ // Make sure matches are sorted by index
+ matches.sort((a, b) => a.index - b.index)
+
if (matches.length === 0) {
return null
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 1e8a1970..9c949db3 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Fix completions not showing for some class attributes when a class function exists in the document ([#1278](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1278))
# 0.14.10
From e85877419954148a8e3efd76ab85bded0ac34d5a Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 20 Mar 2025 18:54:06 -0400
Subject: [PATCH 026/108] 0.14.11
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index f04ee81c..f43e88df 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.10",
+ "version": "0.14.11",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 6082fc86..2fc8c0b4 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.10",
+ "version": "0.14.11",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 9c949db3..9b0498db 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.11
+
- Fix completions not showing for some class attributes when a class function exists in the document ([#1278](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1278))
# 0.14.10
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 2747fd44..038c9133 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.10",
+ "version": "0.14.11",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 3650ed46f3841331d319d93c6a151f603fc86eb8 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 13:55:09 -0400
Subject: [PATCH 027/108] Bump Vitest
---
.../tailwindcss-language-server/package.json | 2 +-
.../tailwindcss-language-service/package.json | 2 +-
pnpm-lock.yaml | 454 ++++++------------
3 files changed, 148 insertions(+), 310 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index f43e88df..63089551 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -89,7 +89,7 @@
"tsconfig-paths": "^4.2.0",
"typescript": "5.3.3",
"vite-tsconfig-paths": "^4.3.1",
- "vitest": "^1.6.1",
+ "vitest": "^3.0.9",
"vscode-css-languageservice": "6.2.9",
"vscode-jsonrpc": "8.2.0",
"vscode-languageclient": "8.1.0",
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 2fc8c0b4..40ddba77 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -52,6 +52,6 @@
"minimist": "^1.2.8",
"tslib": "2.2.0",
"typescript": "^5.3.3",
- "vitest": "^1.6.1"
+ "vitest": "^3.0.9"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 67d58759..6367f5ae 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -198,8 +198,8 @@ importers:
specifier: ^4.3.1
version: 4.3.2(typescript@5.3.3)(vite@5.4.14(@types/node@18.19.43))
vitest:
- specifier: ^1.6.1
- version: 1.6.1(@types/node@18.19.43)
+ specifier: ^3.0.9
+ version: 3.0.9(@types/node@18.19.43)
vscode-css-languageservice:
specifier: 6.2.9
version: 6.2.9
@@ -337,8 +337,8 @@ importers:
specifier: ^5.3.3
version: 5.3.3
vitest:
- specifier: ^1.6.1
- version: 1.6.1(@types/node@18.19.43)
+ specifier: ^3.0.9
+ version: 3.0.9(@types/node@18.19.43)
packages/vscode-tailwindcss:
devDependencies:
@@ -725,10 +725,6 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
- '@jest/schemas@29.6.3':
- resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -874,9 +870,6 @@ packages:
cpu: [x64]
os: [win32]
- '@sinclair/typebox@0.27.8':
- resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
-
'@tailwindcss/aspect-ratio@0.4.2':
resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==}
peerDependencies:
@@ -1042,20 +1035,34 @@ packages:
'@types/ws@8.5.12':
resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==}
- '@vitest/expect@1.6.1':
- resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==}
+ '@vitest/expect@3.0.9':
+ resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==}
+
+ '@vitest/mocker@3.0.9':
+ resolution: {integrity: sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@3.0.9':
+ resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==}
- '@vitest/runner@1.6.1':
- resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==}
+ '@vitest/runner@3.0.9':
+ resolution: {integrity: sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==}
- '@vitest/snapshot@1.6.1':
- resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==}
+ '@vitest/snapshot@3.0.9':
+ resolution: {integrity: sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==}
- '@vitest/spy@1.6.1':
- resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==}
+ '@vitest/spy@3.0.9':
+ resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==}
- '@vitest/utils@1.6.1':
- resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==}
+ '@vitest/utils@3.0.9':
+ resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==}
'@vscode/l10n@0.0.16':
resolution: {integrity: sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==}
@@ -1068,15 +1075,6 @@ packages:
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
- acorn-walk@8.3.4:
- resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
- engines: {node: '>=0.4.0'}
-
- acorn@8.14.0:
- resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -1093,10 +1091,6 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
- ansi-styles@5.2.0:
- resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
- engines: {node: '>=10'}
-
ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
@@ -1125,8 +1119,9 @@ packages:
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
- assertion-error@1.1.0:
- resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
azure-devops-node-api@11.2.0:
resolution: {integrity: sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==}
@@ -1201,9 +1196,9 @@ packages:
caniuse-lite@1.0.30001649:
resolution: {integrity: sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==}
- chai@4.5.0:
- resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
- engines: {node: '>=4'}
+ chai@5.2.0:
+ resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
+ engines: {node: '>=12'}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1213,8 +1208,9 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
- check-error@1.0.3:
- resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
@@ -1262,9 +1258,6 @@ packages:
engines: {node: ^12.20.0 || ^14.13.0 || >=16.0.0}
hasBin: true
- confbox@0.1.8:
- resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
-
cosmiconfig@7.1.0:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
engines: {node: '>=10'}
@@ -1353,8 +1346,8 @@ packages:
babel-plugin-macros:
optional: true
- deep-eql@4.1.4:
- resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
deep-extend@0.6.0:
@@ -1383,10 +1376,6 @@ packages:
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
- diff-sequences@29.6.3:
- resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
@@ -1447,6 +1436,9 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
+ es-module-lexer@1.6.0:
+ resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
+
esbuild-node-externals@1.14.0:
resolution: {integrity: sha512-jMWnTlCII3cLEjR5+u0JRSTJuP+MgbjEHKfwSIAI41NgLQ0ZjfzjchlbEn0r7v2u5gCBMSEYvYlkO7GDG8gG3A==}
engines: {node: '>=12'}
@@ -1474,14 +1466,14 @@ packages:
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
- execa@8.0.1:
- resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
- engines: {node: '>=16.17'}
-
expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
+ expect-type@1.2.0:
+ resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==}
+ engines: {node: '>=12.0.0'}
+
fast-glob@3.2.4:
resolution: {integrity: sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==}
engines: {node: '>=8'}
@@ -1533,9 +1525,6 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
- get-func-name@2.0.2:
- resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
-
get-intrinsic@1.2.4:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
@@ -1543,10 +1532,6 @@ packages:
get-own-enumerable-property-symbols@3.0.2:
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
- get-stream@8.0.1:
- resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
- engines: {node: '>=16'}
-
github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
@@ -1621,10 +1606,6 @@ packages:
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
- human-signals@5.0.0:
- resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
- engines: {node: '>=16.17.0'}
-
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -1695,10 +1676,6 @@ packages:
resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==}
engines: {node: '>=0.10.0'}
- is-stream@3.0.0:
- resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
@@ -1728,9 +1705,6 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
- js-tokens@9.0.1:
- resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
-
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -1778,10 +1752,6 @@ packages:
linkify-it@3.0.3:
resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==}
- local-pkg@0.5.1:
- resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
- engines: {node: '>=14'}
-
locate-path@3.0.0:
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
engines: {node: '>=6'}
@@ -1802,8 +1772,8 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
- loupe@2.3.7:
- resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+ loupe@3.1.3:
+ resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
lru-cache@10.0.1:
resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==}
@@ -1835,9 +1805,6 @@ packages:
resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
- merge-stream@2.0.0:
- resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
-
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -1855,10 +1822,6 @@ packages:
engines: {node: '>=4'}
hasBin: true
- mimic-fn@4.0.0:
- resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
- engines: {node: '>=12'}
-
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
@@ -1900,9 +1863,6 @@ packages:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
- mlly@1.7.4:
- resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
-
moo@0.5.1:
resolution: {integrity: sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==}
@@ -1995,10 +1955,6 @@ packages:
resolution: {integrity: sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==}
engines: {node: ^16.14.0 || >=18.0.0}
- npm-run-path@5.3.0:
- resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
@@ -2017,10 +1973,6 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
- onetime@6.0.0:
- resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
- engines: {node: '>=12'}
-
os-homedir@1.0.2:
resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==}
engines: {node: '>=0.10.0'}
@@ -2041,10 +1993,6 @@ packages:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
- p-limit@5.0.0:
- resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
- engines: {node: '>=18'}
-
p-locate@3.0.0:
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
engines: {node: '>=6'}
@@ -2094,10 +2042,6 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
- path-key@4.0.0:
- resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
- engines: {node: '>=12'}
-
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@@ -2109,14 +2053,12 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
- pathe@1.1.2:
- resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
- pathe@2.0.2:
- resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==}
-
- pathval@1.1.1:
- resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
@@ -2143,9 +2085,6 @@ packages:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
- pkg-types@1.3.1:
- resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
-
pkg-up@3.1.0:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
engines: {node: '>=8'}
@@ -2223,10 +2162,6 @@ packages:
engines: {node: '>=14'}
hasBin: true
- pretty-format@29.7.0:
- resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-
proc-log@3.0.0:
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -2261,9 +2196,6 @@ packages:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
- react-is@18.3.1:
- resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
-
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
@@ -2468,10 +2400,6 @@ packages:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
- strip-final-newline@3.0.0:
- resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
- engines: {node: '>=12'}
-
strip-indent@4.0.0:
resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
engines: {node: '>=12'}
@@ -2480,9 +2408,6 @@ packages:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
- strip-literal@2.1.1:
- resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
-
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2533,12 +2458,19 @@ packages:
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
- tinypool@0.8.4:
- resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==}
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+ tinypool@1.0.2:
+ resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
- tinyspy@2.2.1:
- resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
+ tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
engines: {node: '>=14.0.0'}
tmp-cache@1.1.0:
@@ -2598,10 +2530,6 @@ packages:
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
- type-detect@4.1.0:
- resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
- engines: {node: '>=4'}
-
type-fest@1.4.0:
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
engines: {node: '>=10'}
@@ -2617,9 +2545,6 @@ packages:
uc.micro@1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
- ufo@1.5.4:
- resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
-
underscore@1.13.7:
resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==}
@@ -2651,9 +2576,9 @@ packages:
resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
- vite-node@1.6.1:
- resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==}
- engines: {node: ^18.0.0 || >=20.0.0}
+ vite-node@3.0.9:
+ resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
vite-tsconfig-paths@4.3.2:
@@ -2695,20 +2620,23 @@ packages:
terser:
optional: true
- vitest@1.6.1:
- resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==}
- engines: {node: ^18.0.0 || >=20.0.0}
+ vitest@3.0.9:
+ resolution: {integrity: sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
- '@types/node': ^18.0.0 || >=20.0.0
- '@vitest/browser': 1.6.1
- '@vitest/ui': 1.6.1
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.0.9
+ '@vitest/ui': 3.0.9
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
+ '@types/debug':
+ optional: true
'@types/node':
optional: true
'@vitest/browser':
@@ -2845,10 +2773,6 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
- yocto-queue@1.1.1:
- resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==}
- engines: {node: '>=12.20'}
-
snapshots:
'@alloc/quick-lru@5.2.0': {}
@@ -3035,10 +2959,6 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
- '@jest/schemas@29.6.3':
- dependencies:
- '@sinclair/typebox': 0.27.8
-
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@@ -3162,8 +3082,6 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.34.3':
optional: true
- '@sinclair/typebox@0.27.8': {}
-
'@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.4.17)':
dependencies:
tailwindcss: 3.4.17
@@ -3292,34 +3210,45 @@ snapshots:
dependencies:
'@types/node': 18.19.43
- '@vitest/expect@1.6.1':
+ '@vitest/expect@3.0.9':
+ dependencies:
+ '@vitest/spy': 3.0.9
+ '@vitest/utils': 3.0.9
+ chai: 5.2.0
+ tinyrainbow: 2.0.0
+
+ '@vitest/mocker@3.0.9(vite@5.4.14(@types/node@18.19.43))':
+ dependencies:
+ '@vitest/spy': 3.0.9
+ estree-walker: 3.0.3
+ magic-string: 0.30.17
+ optionalDependencies:
+ vite: 5.4.14(@types/node@18.19.43)
+
+ '@vitest/pretty-format@3.0.9':
dependencies:
- '@vitest/spy': 1.6.1
- '@vitest/utils': 1.6.1
- chai: 4.5.0
+ tinyrainbow: 2.0.0
- '@vitest/runner@1.6.1':
+ '@vitest/runner@3.0.9':
dependencies:
- '@vitest/utils': 1.6.1
- p-limit: 5.0.0
- pathe: 1.1.2
+ '@vitest/utils': 3.0.9
+ pathe: 2.0.3
- '@vitest/snapshot@1.6.1':
+ '@vitest/snapshot@3.0.9':
dependencies:
+ '@vitest/pretty-format': 3.0.9
magic-string: 0.30.17
- pathe: 1.1.2
- pretty-format: 29.7.0
+ pathe: 2.0.3
- '@vitest/spy@1.6.1':
+ '@vitest/spy@3.0.9':
dependencies:
- tinyspy: 2.2.1
+ tinyspy: 3.0.2
- '@vitest/utils@1.6.1':
+ '@vitest/utils@3.0.9':
dependencies:
- diff-sequences: 29.6.3
- estree-walker: 3.0.3
- loupe: 2.3.7
- pretty-format: 29.7.0
+ '@vitest/pretty-format': 3.0.9
+ loupe: 3.1.3
+ tinyrainbow: 2.0.0
'@vscode/l10n@0.0.16': {}
@@ -3350,12 +3279,6 @@ snapshots:
abbrev@1.1.1: {}
- acorn-walk@8.3.4:
- dependencies:
- acorn: 8.14.0
-
- acorn@8.14.0: {}
-
ansi-regex@5.0.1: {}
ansi-regex@6.0.1: {}
@@ -3368,8 +3291,6 @@ snapshots:
dependencies:
color-convert: 2.0.1
- ansi-styles@5.2.0: {}
-
ansi-styles@6.2.1: {}
any-promise@1.3.0: {}
@@ -3389,7 +3310,7 @@ snapshots:
asap@2.0.6: {}
- assertion-error@1.1.0: {}
+ assertion-error@2.0.1: {}
azure-devops-node-api@11.2.0:
dependencies:
@@ -3474,15 +3395,13 @@ snapshots:
caniuse-lite@1.0.30001649: {}
- chai@4.5.0:
+ chai@5.2.0:
dependencies:
- assertion-error: 1.1.0
- check-error: 1.0.3
- deep-eql: 4.1.4
- get-func-name: 2.0.2
- loupe: 2.3.7
- pathval: 1.1.1
- type-detect: 4.1.0
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.3
+ pathval: 2.0.0
chalk@2.4.2:
dependencies:
@@ -3495,9 +3414,7 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
- check-error@1.0.3:
- dependencies:
- get-func-name: 2.0.2
+ check-error@2.1.1: {}
cheerio-select@2.1.0:
dependencies:
@@ -3568,8 +3485,6 @@ snapshots:
tree-kill: 1.2.2
yargs: 16.2.0
- confbox@0.1.8: {}
-
cosmiconfig@7.1.0:
dependencies:
'@types/parse-json': 4.0.2
@@ -3636,9 +3551,7 @@ snapshots:
dedent@1.5.3: {}
- deep-eql@4.1.4:
- dependencies:
- type-detect: 4.1.0
+ deep-eql@5.0.2: {}
deep-extend@0.6.0:
optional: true
@@ -3663,8 +3576,6 @@ snapshots:
didyoumean@1.2.2: {}
- diff-sequences@29.6.3: {}
-
dlv@1.1.3: {}
dom-serializer@2.0.0:
@@ -3721,6 +3632,8 @@ snapshots:
es-errors@1.3.0: {}
+ es-module-lexer@1.6.0: {}
+
esbuild-node-externals@1.14.0(esbuild@0.25.0):
dependencies:
esbuild: 0.25.0
@@ -3789,21 +3702,11 @@ snapshots:
dependencies:
'@types/estree': 1.0.6
- execa@8.0.1:
- dependencies:
- cross-spawn: 7.0.6
- get-stream: 8.0.1
- human-signals: 5.0.0
- is-stream: 3.0.0
- merge-stream: 2.0.0
- npm-run-path: 5.3.0
- onetime: 6.0.0
- signal-exit: 4.1.0
- strip-final-newline: 3.0.0
-
expand-template@2.0.3:
optional: true
+ expect-type@1.2.0: {}
+
fast-glob@3.2.4:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -3861,8 +3764,6 @@ snapshots:
get-caller-file@2.0.5: {}
- get-func-name@2.0.2: {}
-
get-intrinsic@1.2.4:
dependencies:
es-errors: 1.3.0
@@ -3873,8 +3774,6 @@ snapshots:
get-own-enumerable-property-symbols@3.0.2: {}
- get-stream@8.0.1: {}
-
github-from-package@0.0.0:
optional: true
@@ -3950,8 +3849,6 @@ snapshots:
domutils: 3.1.0
entities: 4.5.0
- human-signals@5.0.0: {}
-
ieee754@1.2.1:
optional: true
@@ -4008,8 +3905,6 @@ snapshots:
is-regexp@1.0.0: {}
- is-stream@3.0.0: {}
-
isarray@1.0.0: {}
isexe@2.0.0: {}
@@ -4032,8 +3927,6 @@ snapshots:
js-tokens@4.0.0: {}
- js-tokens@9.0.1: {}
-
json-parse-even-better-errors@2.3.1: {}
json-parse-even-better-errors@3.0.0: {}
@@ -4082,11 +3975,6 @@ snapshots:
dependencies:
uc.micro: 1.0.6
- local-pkg@0.5.1:
- dependencies:
- mlly: 1.7.4
- pkg-types: 1.3.1
-
locate-path@3.0.0:
dependencies:
p-locate: 3.0.0
@@ -4104,9 +3992,7 @@ snapshots:
lodash@4.17.21: {}
- loupe@2.3.7:
- dependencies:
- get-func-name: 2.0.2
+ loupe@3.1.3: {}
lru-cache@10.0.1: {}
@@ -4147,8 +4033,6 @@ snapshots:
type-fest: 1.4.0
yargs-parser: 20.2.9
- merge-stream@2.0.0: {}
-
merge2@1.4.1: {}
micromatch@4.0.7:
@@ -4163,8 +4047,6 @@ snapshots:
mime@1.6.0: {}
- mimic-fn@4.0.0: {}
-
mimic-response@3.1.0:
optional: true
@@ -4201,13 +4083,6 @@ snapshots:
dependencies:
minimist: 1.2.8
- mlly@1.7.4:
- dependencies:
- acorn: 8.14.0
- pathe: 2.0.2
- pkg-types: 1.3.1
- ufo: 1.5.4
-
moo@0.5.1: {}
move-file-cli@3.0.0:
@@ -4302,10 +4177,6 @@ snapshots:
npm-package-arg: 11.0.1
semver: 7.7.1
- npm-run-path@5.3.0:
- dependencies:
- path-key: 4.0.0
-
nth-check@2.1.1:
dependencies:
boolbase: 1.0.0
@@ -4320,10 +4191,6 @@ snapshots:
dependencies:
wrappy: 1.0.2
- onetime@6.0.0:
- dependencies:
- mimic-fn: 4.0.0
-
os-homedir@1.0.2: {}
os-tmpdir@1.0.2: {}
@@ -4341,10 +4208,6 @@ snapshots:
dependencies:
yocto-queue: 0.1.0
- p-limit@5.0.0:
- dependencies:
- yocto-queue: 1.1.1
-
p-locate@3.0.0:
dependencies:
p-limit: 2.3.0
@@ -4389,8 +4252,6 @@ snapshots:
path-key@3.1.1: {}
- path-key@4.0.0: {}
-
path-parse@1.0.7: {}
path-scurry@1.10.1:
@@ -4400,11 +4261,9 @@ snapshots:
path-type@4.0.0: {}
- pathe@1.1.2: {}
-
- pathe@2.0.2: {}
+ pathe@2.0.3: {}
- pathval@1.1.1: {}
+ pathval@2.0.0: {}
pend@1.2.0: {}
@@ -4420,12 +4279,6 @@ snapshots:
pirates@4.0.6: {}
- pkg-types@1.3.1:
- dependencies:
- confbox: 0.1.8
- mlly: 1.7.4
- pathe: 2.0.2
-
pkg-up@3.1.0:
dependencies:
find-up: 3.0.0
@@ -4514,12 +4367,6 @@ snapshots:
prettier@3.2.5: {}
- pretty-format@29.7.0:
- dependencies:
- '@jest/schemas': 29.6.3
- ansi-styles: 5.2.0
- react-is: 18.3.1
-
proc-log@3.0.0: {}
promise-inflight@1.0.1: {}
@@ -4551,8 +4398,6 @@ snapshots:
strip-json-comments: 2.0.1
optional: true
- react-is@18.3.1: {}
-
read-cache@1.0.0:
dependencies:
pify: 2.3.0
@@ -4797,8 +4642,6 @@ snapshots:
strip-bom@3.0.0: {}
- strip-final-newline@3.0.0: {}
-
strip-indent@4.0.0:
dependencies:
min-indent: 1.0.1
@@ -4806,10 +4649,6 @@ snapshots:
strip-json-comments@2.0.1:
optional: true
- strip-literal@2.1.1:
- dependencies:
- js-tokens: 9.0.1
-
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
@@ -4892,9 +4731,13 @@ snapshots:
tinybench@2.9.0: {}
- tinypool@0.8.4: {}
+ tinyexec@0.3.2: {}
- tinyspy@2.2.1: {}
+ tinypool@1.0.2: {}
+
+ tinyrainbow@2.0.0: {}
+
+ tinyspy@3.0.2: {}
tmp-cache@1.1.0: {}
@@ -4935,8 +4778,6 @@ snapshots:
tunnel@0.0.6: {}
- type-detect@4.1.0: {}
-
type-fest@1.4.0: {}
typed-rest-client@1.8.11:
@@ -4949,8 +4790,6 @@ snapshots:
uc.micro@1.0.6: {}
- ufo@1.5.4: {}
-
underscore@1.13.7: {}
undici-types@5.26.5: {}
@@ -4978,12 +4817,12 @@ snapshots:
dependencies:
builtins: 5.0.1
- vite-node@1.6.1(@types/node@18.19.43):
+ vite-node@3.0.9(@types/node@18.19.43):
dependencies:
cac: 6.7.14
debug: 4.4.0
- pathe: 1.1.2
- picocolors: 1.1.1
+ es-module-lexer: 1.6.0
+ pathe: 2.0.3
vite: 5.4.14(@types/node@18.19.43)
transitivePeerDependencies:
- '@types/node'
@@ -5016,33 +4855,34 @@ snapshots:
'@types/node': 18.19.43
fsevents: 2.3.3
- vitest@1.6.1(@types/node@18.19.43):
+ vitest@3.0.9(@types/node@18.19.43):
dependencies:
- '@vitest/expect': 1.6.1
- '@vitest/runner': 1.6.1
- '@vitest/snapshot': 1.6.1
- '@vitest/spy': 1.6.1
- '@vitest/utils': 1.6.1
- acorn-walk: 8.3.4
- chai: 4.5.0
+ '@vitest/expect': 3.0.9
+ '@vitest/mocker': 3.0.9(vite@5.4.14(@types/node@18.19.43))
+ '@vitest/pretty-format': 3.0.9
+ '@vitest/runner': 3.0.9
+ '@vitest/snapshot': 3.0.9
+ '@vitest/spy': 3.0.9
+ '@vitest/utils': 3.0.9
+ chai: 5.2.0
debug: 4.4.0
- execa: 8.0.1
- local-pkg: 0.5.1
+ expect-type: 1.2.0
magic-string: 0.30.17
- pathe: 1.1.2
- picocolors: 1.1.1
+ pathe: 2.0.3
std-env: 3.8.0
- strip-literal: 2.1.1
tinybench: 2.9.0
- tinypool: 0.8.4
+ tinyexec: 0.3.2
+ tinypool: 1.0.2
+ tinyrainbow: 2.0.0
vite: 5.4.14(@types/node@18.19.43)
- vite-node: 1.6.1(@types/node@18.19.43)
+ vite-node: 3.0.9(@types/node@18.19.43)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 18.19.43
transitivePeerDependencies:
- less
- lightningcss
+ - msw
- sass
- sass-embedded
- stylus
@@ -5172,5 +5012,3 @@ snapshots:
buffer-crc32: 0.2.13
yocto-queue@0.1.0: {}
-
- yocto-queue@1.1.1: {}
From d909c7716a7aabdf29d99b8f924429ecc67a06bc Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 12:51:45 -0400
Subject: [PATCH 028/108] Bump versions
---
.../v4/auto-content-split/package-lock.json | 104 +++++++++---------
.../v4/auto-content-split/package.json | 4 +-
.../v4/auto-content/package-lock.json | 104 +++++++++---------
.../fixtures/v4/auto-content/package.json | 4 +-
.../tests/fixtures/v4/basic/package-lock.json | 8 +-
.../tests/fixtures/v4/basic/package.json | 2 +-
.../v4/css-loading-js/package-lock.json | 8 +-
.../fixtures/v4/css-loading-js/package.json | 2 +-
.../v4/custom-source/package-lock.json | 8 +-
.../fixtures/v4/custom-source/package.json | 2 +-
.../v4/dependencies/package-lock.json | 8 +-
.../fixtures/v4/dependencies/package.json | 2 +-
.../v4/invalid-import-order/package-lock.json | 8 +-
.../v4/invalid-import-order/package.json | 2 +-
.../v4/missing-files/package-lock.json | 8 +-
.../fixtures/v4/missing-files/package.json | 2 +-
.../v4/multi-config/package-lock.json | 8 +-
.../fixtures/v4/multi-config/package.json | 2 +-
.../v4/path-mappings/package-lock.json | 8 +-
.../fixtures/v4/path-mappings/package.json | 2 +-
.../fixtures/v4/with-prefix/package-lock.json | 8 +-
.../fixtures/v4/with-prefix/package.json | 2 +-
.../fixtures/v4/workspaces/package-lock.json | 8 +-
.../tests/fixtures/v4/workspaces/package.json | 2 +-
24 files changed, 158 insertions(+), 158 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
index 50cb1adf..b829f613 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
@@ -5,36 +5,36 @@
"packages": {
"": {
"dependencies": {
- "@tailwindcss/oxide": "^4.0.0-beta.10",
- "tailwindcss": "^4.0.0-beta.10"
+ "@tailwindcss/oxide": "^4.0.15",
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.0-beta.10.tgz",
- "integrity": "sha512-r6tfUMqtkdX2Bwuhury6/wHfrWK2R8aRHMAVcppCKr1/SE2uIlPCGcOzkuybWlRU9OwjHk1t+dVgEcC0igSZ7g==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.15.tgz",
+ "integrity": "sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==",
"license": "MIT",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.0.0-beta.10",
- "@tailwindcss/oxide-darwin-arm64": "4.0.0-beta.10",
- "@tailwindcss/oxide-darwin-x64": "4.0.0-beta.10",
- "@tailwindcss/oxide-freebsd-x64": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm64-musl": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-x64-gnu": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-x64-musl": "4.0.0-beta.10",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.0.0-beta.10",
- "@tailwindcss/oxide-win32-x64-msvc": "4.0.0-beta.10"
+ "@tailwindcss/oxide-android-arm64": "4.0.15",
+ "@tailwindcss/oxide-darwin-arm64": "4.0.15",
+ "@tailwindcss/oxide-darwin-x64": "4.0.15",
+ "@tailwindcss/oxide-freebsd-x64": "4.0.15",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.15",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.0.15",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.0.15",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.0.15",
+ "@tailwindcss/oxide-linux-x64-musl": "4.0.15",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.0.15",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.0.15"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.0-beta.10.tgz",
- "integrity": "sha512-wQ3NcFTToQlippJqxNsPtUjkzldcL+RciGvW5tDRGq7yXt/giND292+joshNp55Bi51rXRLXfAU19PV0JP9IGQ==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.15.tgz",
+ "integrity": "sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==",
"cpu": [
"arm64"
],
@@ -48,9 +48,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.0-beta.10.tgz",
- "integrity": "sha512-g65kSY1n0AdhhWXchTqmyv+jblDMM+eXM2SJaBidcGicz3wYm0a548i0lVIn3Lt8f72akhftz9YKHaL3riq5AA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.15.tgz",
+ "integrity": "sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==",
"cpu": [
"arm64"
],
@@ -64,9 +64,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.0-beta.10.tgz",
- "integrity": "sha512-edQRV1hucPU5pIh1j4pXcqnwdxjme0i4P2tMqBvSTM9UTGQmvxn6/QM2oos7htn3dfgqIkvMBb0y6u1mnBLwKw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.15.tgz",
+ "integrity": "sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==",
"cpu": [
"x64"
],
@@ -80,9 +80,9 @@
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.0-beta.10.tgz",
- "integrity": "sha512-okok/IcX9jh85T9mzLOYGr8996KNm+ZU0t1mwibWvW36jBM0oIfq3TVw3xI3M3OkuYj1XpALfcO4Kr8ZZrt2cw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.15.tgz",
+ "integrity": "sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==",
"cpu": [
"x64"
],
@@ -96,9 +96,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.0-beta.10.tgz",
- "integrity": "sha512-gfjDqO/3fWkKPd2Lv3rj0vW2CMHRmWH50qFJ29vAkhtteg7oPB/NU1Z4K5YkSpul0Pgcfixv6YfPQpzBkrM7Nw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.15.tgz",
+ "integrity": "sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==",
"cpu": [
"arm"
],
@@ -112,9 +112,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.0-beta.10.tgz",
- "integrity": "sha512-/LgdJ+XBZK5ZgyvxiMEJTrMj36GMEhRGTtuHGJmxJDcvv4VFbCzDmNN8H/YC9dtCF0OEOfsCCQxEeKTfUYIuJg==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.15.tgz",
+ "integrity": "sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==",
"cpu": [
"arm64"
],
@@ -128,9 +128,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.0-beta.10.tgz",
- "integrity": "sha512-pIoXoN6BZ3gDDDBBW2mZtLLNMTB3UW1F/uRGMTuPUYl8cERUlXOYek4v2veCMwzTNf5l4FrgwPm8G5rsx9qNiw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.15.tgz",
+ "integrity": "sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==",
"cpu": [
"arm64"
],
@@ -144,9 +144,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.0-beta.10.tgz",
- "integrity": "sha512-mVXCIesysMgzU7XerH2VWN6R9LEdr+MUZ1LAvUx1wDcjawMPGWbzjyVWzbRNgWxQLPi71YuFT9hU3Srewfb6dw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.15.tgz",
+ "integrity": "sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==",
"cpu": [
"x64"
],
@@ -160,9 +160,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.0-beta.10.tgz",
- "integrity": "sha512-TPszoPUQbXTy76kgi38ohCpT9SFMjxHaLKO3z6SUZis2+HURl7H6Z8rS/PlZwNCawv5Dg+UKsYfh1UwvfepXmQ==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.15.tgz",
+ "integrity": "sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==",
"cpu": [
"x64"
],
@@ -176,9 +176,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.0-beta.10.tgz",
- "integrity": "sha512-Gq4bQm1QsaMIX95/zkpnI3J+8CMAgZSWLIYeAq2ehom9AG/Qe9AaT7dDzIzI68uovqwScnKWiJJLo1SfHveoRA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.15.tgz",
+ "integrity": "sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==",
"cpu": [
"arm64"
],
@@ -192,9 +192,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.0-beta.10.tgz",
- "integrity": "sha512-rzKGE0TvLyADYaAq50oYVGtFXZ9QZEZfeaEqrRdUkK4HMNr5kv66bv7k46JN9260/K8cHp60ISPwLObHloi3zA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.15.tgz",
+ "integrity": "sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==",
"cpu": [
"x64"
],
@@ -208,9 +208,9 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
index 8f5d1bbd..908f1d2f 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
@@ -1,6 +1,6 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10",
- "@tailwindcss/oxide": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15",
+ "@tailwindcss/oxide": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
index 7b41a366..6b32300e 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
@@ -5,36 +5,36 @@
"packages": {
"": {
"dependencies": {
- "@tailwindcss/oxide": "^4.0.0-beta.10",
- "tailwindcss": "^4.0.0-beta.10"
+ "@tailwindcss/oxide": "^4.0.15",
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.0-beta.10.tgz",
- "integrity": "sha512-r6tfUMqtkdX2Bwuhury6/wHfrWK2R8aRHMAVcppCKr1/SE2uIlPCGcOzkuybWlRU9OwjHk1t+dVgEcC0igSZ7g==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.15.tgz",
+ "integrity": "sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==",
"license": "MIT",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.0.0-beta.10",
- "@tailwindcss/oxide-darwin-arm64": "4.0.0-beta.10",
- "@tailwindcss/oxide-darwin-x64": "4.0.0-beta.10",
- "@tailwindcss/oxide-freebsd-x64": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-arm64-musl": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-x64-gnu": "4.0.0-beta.10",
- "@tailwindcss/oxide-linux-x64-musl": "4.0.0-beta.10",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.0.0-beta.10",
- "@tailwindcss/oxide-win32-x64-msvc": "4.0.0-beta.10"
+ "@tailwindcss/oxide-android-arm64": "4.0.15",
+ "@tailwindcss/oxide-darwin-arm64": "4.0.15",
+ "@tailwindcss/oxide-darwin-x64": "4.0.15",
+ "@tailwindcss/oxide-freebsd-x64": "4.0.15",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.15",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.0.15",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.0.15",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.0.15",
+ "@tailwindcss/oxide-linux-x64-musl": "4.0.15",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.0.15",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.0.15"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.0-beta.10.tgz",
- "integrity": "sha512-wQ3NcFTToQlippJqxNsPtUjkzldcL+RciGvW5tDRGq7yXt/giND292+joshNp55Bi51rXRLXfAU19PV0JP9IGQ==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.15.tgz",
+ "integrity": "sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==",
"cpu": [
"arm64"
],
@@ -48,9 +48,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.0-beta.10.tgz",
- "integrity": "sha512-g65kSY1n0AdhhWXchTqmyv+jblDMM+eXM2SJaBidcGicz3wYm0a548i0lVIn3Lt8f72akhftz9YKHaL3riq5AA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.15.tgz",
+ "integrity": "sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==",
"cpu": [
"arm64"
],
@@ -64,9 +64,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.0-beta.10.tgz",
- "integrity": "sha512-edQRV1hucPU5pIh1j4pXcqnwdxjme0i4P2tMqBvSTM9UTGQmvxn6/QM2oos7htn3dfgqIkvMBb0y6u1mnBLwKw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.15.tgz",
+ "integrity": "sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==",
"cpu": [
"x64"
],
@@ -80,9 +80,9 @@
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.0-beta.10.tgz",
- "integrity": "sha512-okok/IcX9jh85T9mzLOYGr8996KNm+ZU0t1mwibWvW36jBM0oIfq3TVw3xI3M3OkuYj1XpALfcO4Kr8ZZrt2cw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.15.tgz",
+ "integrity": "sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==",
"cpu": [
"x64"
],
@@ -96,9 +96,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.0-beta.10.tgz",
- "integrity": "sha512-gfjDqO/3fWkKPd2Lv3rj0vW2CMHRmWH50qFJ29vAkhtteg7oPB/NU1Z4K5YkSpul0Pgcfixv6YfPQpzBkrM7Nw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.15.tgz",
+ "integrity": "sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==",
"cpu": [
"arm"
],
@@ -112,9 +112,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.0-beta.10.tgz",
- "integrity": "sha512-/LgdJ+XBZK5ZgyvxiMEJTrMj36GMEhRGTtuHGJmxJDcvv4VFbCzDmNN8H/YC9dtCF0OEOfsCCQxEeKTfUYIuJg==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.15.tgz",
+ "integrity": "sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==",
"cpu": [
"arm64"
],
@@ -128,9 +128,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.0-beta.10.tgz",
- "integrity": "sha512-pIoXoN6BZ3gDDDBBW2mZtLLNMTB3UW1F/uRGMTuPUYl8cERUlXOYek4v2veCMwzTNf5l4FrgwPm8G5rsx9qNiw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.15.tgz",
+ "integrity": "sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==",
"cpu": [
"arm64"
],
@@ -144,9 +144,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.0-beta.10.tgz",
- "integrity": "sha512-mVXCIesysMgzU7XerH2VWN6R9LEdr+MUZ1LAvUx1wDcjawMPGWbzjyVWzbRNgWxQLPi71YuFT9hU3Srewfb6dw==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.15.tgz",
+ "integrity": "sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==",
"cpu": [
"x64"
],
@@ -160,9 +160,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.0-beta.10.tgz",
- "integrity": "sha512-TPszoPUQbXTy76kgi38ohCpT9SFMjxHaLKO3z6SUZis2+HURl7H6Z8rS/PlZwNCawv5Dg+UKsYfh1UwvfepXmQ==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.15.tgz",
+ "integrity": "sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==",
"cpu": [
"x64"
],
@@ -176,9 +176,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.0-beta.10.tgz",
- "integrity": "sha512-Gq4bQm1QsaMIX95/zkpnI3J+8CMAgZSWLIYeAq2ehom9AG/Qe9AaT7dDzIzI68uovqwScnKWiJJLo1SfHveoRA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.15.tgz",
+ "integrity": "sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==",
"cpu": [
"arm64"
],
@@ -192,9 +192,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.0-beta.10.tgz",
- "integrity": "sha512-rzKGE0TvLyADYaAq50oYVGtFXZ9QZEZfeaEqrRdUkK4HMNr5kv66bv7k46JN9260/K8cHp60ISPwLObHloi3zA==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.15.tgz",
+ "integrity": "sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==",
"cpu": [
"x64"
],
@@ -208,9 +208,9 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
index 8f5d1bbd..908f1d2f 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
@@ -1,6 +1,6 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10",
- "@tailwindcss/oxide": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15",
+ "@tailwindcss/oxide": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
index ce144e37..6275c3dc 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
index 81dca7a7..5089dc65 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
index 2f24caae..ce004f1d 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
index a08734a7..555ee660 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
index 4a96d074..24d978d8 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
index 0ad59a4e..ed4d2d9a 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
index 5a7fd4c6..c4664645 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
index f73ee08a..651bf7c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
index b2ac9b4f..1e5486ae 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
index 90a7d003..b6cb53b1 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
index 76d48561..b622b7b7 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
@@ -8,7 +8,7 @@
"packages/*"
],
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
},
"node_modules/@private/admin": {
@@ -32,9 +32,9 @@
"link": true
},
"node_modules/tailwindcss": {
- "version": "4.0.0-beta.10",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-beta.10.tgz",
- "integrity": "sha512-QphgB58oviLYkSWu74+5S2FU8sREus9wBEzWWi5iSxnBrgzfVFp7pIeAPbQ7gq1WXnbllCipsTjK3yT+Do4y0Q==",
+ "version": "4.0.15",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
+ "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
"license": "MIT"
},
"packages/admin": {
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
index fac98d06..aa5e54db 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
@@ -3,6 +3,6 @@
"packages/*"
],
"dependencies": {
- "tailwindcss": "^4.0.0-beta.10"
+ "tailwindcss": "^4.0.15"
}
}
From 7593e824b2186dd3e4241a26729cd2638e0d40b9 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 12:51:50 -0400
Subject: [PATCH 029/108] Update tests
---
.../tests/completions/completions.test.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index 96f8188a..dbe9a352 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -313,8 +313,8 @@ withFixture('v4/basic', (c) => {
let result = await completion({ lang, text, position, settings })
let textEdit = expect.objectContaining({ range: { start: position, end: position } })
- expect(result.items.length).toBe(12314)
- expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(304)
+ expect(result.items.length).toBe(13172)
+ expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(317)
expect(result).toEqual({
isIncomplete: false,
items: expect.arrayContaining([
From 405733ba59e1d2d51220f1e5d2a710def1ecef02 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 13:28:17 -0400
Subject: [PATCH 030/108] Refactor
---
.../tailwindcss-language-server/src/testing/index.ts | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts
index 21296fc7..a0516d9a 100644
--- a/packages/tailwindcss-language-server/src/testing/index.ts
+++ b/packages/tailwindcss-language-server/src/testing/index.ts
@@ -9,9 +9,14 @@ export interface TestUtils {
root: string
}
+export interface StorageSymlink {
+ [IS_A_SYMLINK]: true
+ filepath: string
+}
+
export interface Storage {
/** A list of files and their content */
- [filePath: string]: string | Uint8Array | { [IS_A_SYMLINK]: true; filepath: string }
+ [filePath: string]: string | Uint8Array | StorageSymlink
}
export interface TestConfig {
@@ -70,7 +75,7 @@ async function setup(config: TestConfig): Promise {
}
const IS_A_SYMLINK = Symbol('is-a-symlink')
-export const symlinkTo = function (filepath: string) {
+export function symlinkTo(filepath: string): StorageSymlink {
return {
[IS_A_SYMLINK]: true as const,
filepath,
From 765ebce4c8a719a115f935352eb5090fef91e776 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 14:18:50 -0400
Subject: [PATCH 031/108] Fix content detection in later Oxide versions (#1280)
This should improve project selection for files in a workspace with
multiple projects. Though probably only a tiny bit.
---
.../tailwindcss-language-server/package.json | 2 +-
.../tailwindcss-language-server/src/oxide.ts | 4 +-
.../src/project-locator.test.ts | 45 ++++----
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
pnpm-lock.yaml | 100 ++++++++++--------
5 files changed, 77 insertions(+), 76 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 63089551..d31549e7 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -40,7 +40,7 @@
"@tailwindcss/forms": "0.5.3",
"@tailwindcss/language-service": "workspace:*",
"@tailwindcss/line-clamp": "0.4.2",
- "@tailwindcss/oxide": "^4.0.0-alpha.19",
+ "@tailwindcss/oxide": "^4.0.15",
"@tailwindcss/typography": "0.5.7",
"@types/braces": "3.0.1",
"@types/color-name": "^1.1.3",
diff --git a/packages/tailwindcss-language-server/src/oxide.ts b/packages/tailwindcss-language-server/src/oxide.ts
index bb8700ff..5dad7553 100644
--- a/packages/tailwindcss-language-server/src/oxide.ts
+++ b/packages/tailwindcss-language-server/src/oxide.ts
@@ -68,7 +68,7 @@ async function loadOxideAtPath(id: string): Promise {
let oxide = await import(id)
// This is a much older, unsupported version of Oxide before v4.0.0-alpha.1
- if (!oxide.scanDir) return null
+ if (!oxide.scanDir && !oxide.Scanner) return null
return oxide
}
@@ -101,7 +101,7 @@ interface ScanResult {
* For example, the `sources` option is ignored before v4.0.0-alpha.19.
*/
export async function scan(options: ScanOptions): Promise {
- const oxide = await loadOxideAtPath(options.oxidePath)
+ let oxide = await loadOxideAtPath(options.oxidePath)
if (!oxide) return null
// V1
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index 6414a099..f350dba1 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -9,7 +9,7 @@ import { css, defineTest, js, json, scss, Storage, TestUtils } from './testing'
let settings: Settings = {
tailwindCSS: {
files: {
- exclude: [],
+ exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'],
},
},
} as any
@@ -114,10 +114,7 @@ testFixture('v4/workspaces', [
{
config: 'packages/admin/app.css',
selectors: [
- '{URL}/node_modules/tailwindcss/**',
- '{URL}/node_modules/tailwindcss/index.css',
- '{URL}/node_modules/tailwindcss/theme.css',
- '{URL}/node_modules/tailwindcss/utilities.css',
+ '{URL}/packages/admin/*',
'{URL}/packages/admin/**',
'{URL}/packages/admin/app.css',
'{URL}/packages/admin/package.json',
@@ -126,15 +123,12 @@ testFixture('v4/workspaces', [
{
config: 'packages/web/app.css',
selectors: [
- '{URL}/node_modules/tailwindcss/**',
- '{URL}/node_modules/tailwindcss/index.css',
- '{URL}/node_modules/tailwindcss/theme.css',
- '{URL}/node_modules/tailwindcss/utilities.css',
'{URL}/packages/style-export/**',
'{URL}/packages/style-export/lib.css',
'{URL}/packages/style-export/theme.css',
'{URL}/packages/style-main-field/**',
'{URL}/packages/style-main-field/lib.css',
+ '{URL}/packages/web/*',
'{URL}/packages/web/**',
'{URL}/packages/web/app.css',
'{URL}/packages/web/package.json',
@@ -147,28 +141,24 @@ testFixture('v4/auto-content', [
{
config: 'src/app.css',
content: [
+ '{URL}/*',
'{URL}/package.json',
'{URL}/src/index.html',
'{URL}/src/components/example.html',
- '{URL}/src/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
+ '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
],
},
])
testFixture('v4/auto-content-split', [
- //
- {
- // TODO: This should _probably_ not be present
- config: 'node_modules/tailwindcss/index.css',
- content: [],
- },
{
config: 'src/app.css',
content: [
+ '{URL}/*',
'{URL}/package.json',
'{URL}/src/index.html',
'{URL}/src/components/example.html',
- '{URL}/src/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
+ '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
],
},
])
@@ -178,23 +168,23 @@ testFixture('v4/custom-source', [
{
config: 'admin/app.css',
content: [
- '{URL}/admin/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
- '{URL}/admin/**/*.bin',
+ '{URL}/*',
'{URL}/admin/foo.bin',
+ '{URL}/admin/{**/*.bin,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
'{URL}/package.json',
'{URL}/shared.html',
- '{URL}/web/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
+ '{URL}/web/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
],
},
{
config: 'web/app.css',
content: [
- '{URL}/admin/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
- '{URL}/web/*.bin',
- '{URL}/web/bar.bin',
+ '{URL}/*',
+ '{URL}/admin/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
'{URL}/package.json',
'{URL}/shared.html',
- '{URL}/web/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
+ '{URL}/web/bar.bin',
+ '{URL}/web/{**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue},*.bin}',
],
},
])
@@ -203,7 +193,7 @@ testFixture('v4/missing-files', [
//
{
config: 'app.css',
- content: ['{URL}/package.json'],
+ content: ['{URL}/*', '{URL}/package.json'],
},
])
@@ -212,8 +202,9 @@ testFixture('v4/path-mappings', [
{
config: 'app.css',
content: [
+ '{URL}/*',
'{URL}/package.json',
- '{URL}/src/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
+ '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
'{URL}/src/a/my-config.ts',
'{URL}/src/a/my-plugin.ts',
'{URL}/tsconfig.json',
@@ -225,7 +216,7 @@ testFixture('v4/invalid-import-order', [
//
{
config: 'tailwind.css',
- content: ['{URL}/package.json'],
+ content: ['{URL}/*', '{URL}/package.json'],
},
])
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 9b0498db..0ec6c3a4 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Fix content detection when using v4.0+ ([#1280](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1280))
# 0.14.11
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6367f5ae..ab264604 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,8 +51,8 @@ importers:
specifier: 0.4.2
version: 0.4.2(tailwindcss@3.4.17)
'@tailwindcss/oxide':
- specifier: ^4.0.0-alpha.19
- version: 4.0.0-alpha.19
+ specifier: ^4.0.15
+ version: 4.0.15
'@tailwindcss/typography':
specifier: 0.5.7
version: 0.5.7(tailwindcss@3.4.17)
@@ -890,68 +890,74 @@ packages:
peerDependencies:
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
- '@tailwindcss/oxide-android-arm64@4.0.0-alpha.19':
- resolution: {integrity: sha512-NhpXem1j7g0uSGyLucmMj0VVQMeUrWc6kR/Ymnri3tpw2eaykgFYwLfdnI7jdJRxUxa/nNJip9yBJ3diZXl60w==}
+ '@tailwindcss/oxide-android-arm64@4.0.15':
+ resolution: {integrity: sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.0.0-alpha.19':
- resolution: {integrity: sha512-KCdalT+huX2cW9snNmPr+B66V91cSzIobBCXVgYCPCh0NZF4ueKu+X+kQN2gFxurDUm/D+aKW/0rQUIsUmFpdQ==}
+ '@tailwindcss/oxide-darwin-arm64@4.0.15':
+ resolution: {integrity: sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.0.0-alpha.19':
- resolution: {integrity: sha512-ETOWA08loUmOVTEa3zhRhY8HyqdGtR9DNhXdrRZBi67ZwCAmA+jg5B+mZaYeQJ6CjETx07BnhcGmmxGz3/6c8w==}
+ '@tailwindcss/oxide-darwin-x64@4.0.15':
+ resolution: {integrity: sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.0.0-alpha.19':
- resolution: {integrity: sha512-uB0rYLpqPnmyqtYSKHu1AtnHeerNcVH+de0sIufGCBDFGYNxmW8feCKNZwo6r7U/Fzg+AF9BOjwvdvd4yLfQ8g==}
+ '@tailwindcss/oxide-freebsd-x64@4.0.15':
+ resolution: {integrity: sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.0-alpha.19':
- resolution: {integrity: sha512-hIfm6DNh18rkz2PFRsQANINH0tpso6/vaU8p0Qw7rgYqqrxJTRpyLVsnvx3ahMOplJrDT6Z+Nfak8udnZN2C/Q==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
+ resolution: {integrity: sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.0.0-alpha.19':
- resolution: {integrity: sha512-n1Hr+8Hup2GLmeonQy9ydZxMBCs0FR1rcv4K7AHip+6PbD0se8k9LBIZac3OguFNj2hTehiadaiRb18rsVUw0g==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
+ resolution: {integrity: sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.0.0-alpha.19':
- resolution: {integrity: sha512-KhwthLZh9Js3t5URkuRURw45iU3rSh9vhuHRaV4KQT10ZFiXQMUlFfMKJyxRMcgC2fcL5vsiqwjOaMwp7Y8vsQ==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
+ resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.0.0-alpha.19':
- resolution: {integrity: sha512-qqEuULSiczyZkdWVzwkiiFyOYqx5RR2De75iwYREzXUuHRHval1ep2qO7tvZdgt37t2vgjoQwaPA6zO+JGUa+Q==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
+ resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.0.0-alpha.19':
- resolution: {integrity: sha512-JKTYCiNz83sYl2FgKJk3dL11FS4dAj7Rgmuz3TVANmeMYTBtwmFPthoH0qAmF+hjPJgT5Ne7lSwplfuHJAD3MQ==}
+ '@tailwindcss/oxide-linux-x64-musl@4.0.15':
+ resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-win32-x64-msvc@4.0.0-alpha.19':
- resolution: {integrity: sha512-D43tji14+i/GhJn5YZX8c2FXFmAqlI6DDGX8caUM35dga/uT+sJUfLJ84YigJyvAdSF1gBVdm7MvhYRcVYHOwg==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
+ resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
+ resolution: {integrity: sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.0.0-alpha.19':
- resolution: {integrity: sha512-DdkrVz/MKPoe9v7W3c0+SEFKRDIPMSsxgN7gPC+xeTnTL4BGoT5b1EiVGFuXWEyLbDmWztuN6z75Yuze2BwvMQ==}
+ '@tailwindcss/oxide@4.0.15':
+ resolution: {integrity: sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==}
engines: {node: '>= 10'}
'@tailwindcss/typography@0.5.7':
@@ -3099,48 +3105,52 @@ snapshots:
dependencies:
tailwindcss: 3.4.17
- '@tailwindcss/oxide-android-arm64@4.0.0-alpha.19':
+ '@tailwindcss/oxide-android-arm64@4.0.15':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.0.15':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.0.0-alpha.19':
+ '@tailwindcss/oxide-darwin-x64@4.0.15':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.0.0-alpha.19':
+ '@tailwindcss/oxide-freebsd-x64@4.0.15':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.0.0-alpha.19':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.0-alpha.19':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.0.0-alpha.19':
+ '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.0.0-alpha.19':
+ '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.0.0-alpha.19':
+ '@tailwindcss/oxide-linux-x64-musl@4.0.15':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.0.0-alpha.19':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.0.0-alpha.19':
+ '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
optional: true
- '@tailwindcss/oxide@4.0.0-alpha.19':
+ '@tailwindcss/oxide@4.0.15':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.0.0-alpha.19
- '@tailwindcss/oxide-darwin-arm64': 4.0.0-alpha.19
- '@tailwindcss/oxide-darwin-x64': 4.0.0-alpha.19
- '@tailwindcss/oxide-freebsd-x64': 4.0.0-alpha.19
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.0-alpha.19
- '@tailwindcss/oxide-linux-arm64-gnu': 4.0.0-alpha.19
- '@tailwindcss/oxide-linux-arm64-musl': 4.0.0-alpha.19
- '@tailwindcss/oxide-linux-x64-gnu': 4.0.0-alpha.19
- '@tailwindcss/oxide-linux-x64-musl': 4.0.0-alpha.19
- '@tailwindcss/oxide-win32-x64-msvc': 4.0.0-alpha.19
+ '@tailwindcss/oxide-android-arm64': 4.0.15
+ '@tailwindcss/oxide-darwin-arm64': 4.0.15
+ '@tailwindcss/oxide-darwin-x64': 4.0.15
+ '@tailwindcss/oxide-freebsd-x64': 4.0.15
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.15
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.0.15
+ '@tailwindcss/oxide-linux-arm64-musl': 4.0.15
+ '@tailwindcss/oxide-linux-x64-gnu': 4.0.15
+ '@tailwindcss/oxide-linux-x64-musl': 4.0.15
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.0.15
+ '@tailwindcss/oxide-win32-x64-msvc': 4.0.15
'@tailwindcss/typography@0.5.7(tailwindcss@3.4.17)':
dependencies:
From f5bcefc6dc8348ac3b964d0ea0bb321b20e08c2a Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 14:19:44 -0400
Subject: [PATCH 032/108] Ensure file exclusions always work on Windows (#1281)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Sometimes exclusions weren't processed correctly on Windows. This cause
additional results to show up in file path completions in things like
`@config`, `@plugim`, `@source`, etc…
---
.../tailwindcss-language-server/src/util/isExcluded.ts | 10 +++++++++-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/packages/tailwindcss-language-server/src/util/isExcluded.ts b/packages/tailwindcss-language-server/src/util/isExcluded.ts
index beb4115a..df998e7f 100644
--- a/packages/tailwindcss-language-server/src/util/isExcluded.ts
+++ b/packages/tailwindcss-language-server/src/util/isExcluded.ts
@@ -3,6 +3,7 @@ import * as path from 'node:path'
import type { TextDocument } from 'vscode-languageserver-textdocument'
import type { State } from '@tailwindcss/language-service/src/util/state'
import { getFileFsPath } from './uri'
+import { normalizePath, normalizeDriveLetter } from '../utils'
export default async function isExcluded(
state: State,
@@ -11,8 +12,15 @@ export default async function isExcluded(
): Promise {
let settings = await state.editor.getConfiguration(document.uri)
+ file = normalizePath(file)
+ file = normalizeDriveLetter(file)
+
for (let pattern of settings.tailwindCSS.files.exclude) {
- if (picomatch(path.join(state.editor.folder, pattern))(file)) {
+ pattern = path.join(state.editor.folder, pattern)
+ pattern = normalizePath(pattern)
+ pattern = normalizeDriveLetter(pattern)
+
+ if (picomatch(pattern)(file)) {
return true
}
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 0ec6c3a4..de81de55 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- Fix content detection when using v4.0+ ([#1280](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1280))
+- Ensure file exclusions always work on Windows ([#1281](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1281))
# 0.14.11
From 8b6eafa51a6669bb9f689c1da65eba4739da1b3c Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Sat, 22 Mar 2025 14:28:10 -0400
Subject: [PATCH 033/108] Add CI testing matrix (#989)
This is starting point for setting up CI on PRs (they already run before
publishing pre-release or release versions).
This is also intended to test multiple Node versions _and_ Windows.
---
.github/workflows/ci.yml | 32 +++++++++++++
.../src/project-locator.test.ts | 13 +++--
.../src/testing/index.ts | 22 +++++++--
.../document-links/document-links.test.js | 47 ++++++-------------
.../tests/env/v4.test.js | 2 +-
5 files changed, 75 insertions(+), 41 deletions(-)
create mode 100644 .github/workflows/ci.yml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..f64cfa03
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,32 @@
+name: Run Tests
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ tests:
+ strategy:
+ matrix:
+ node: [18, 20, 22, 23]
+ os: [ubuntu-latest, macos-latest, windows-latest]
+
+ runs-on: ${{ matrix.os }}
+ name: Run Tests - Node v${{ matrix.node }} / ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ - uses: actions/setup-node@v4
+ with:
+ cache: 'pnpm'
+ node-version: ${{ matrix.node-version }}
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Run tests
+ run: |
+ cd packages/tailwindcss-language-server &&
+ pnpm run build &&
+ pnpm run test
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index f350dba1..52af6446 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -5,6 +5,7 @@ import { URL, fileURLToPath } from 'url'
import { Settings } from '@tailwindcss/language-service/src/util/state'
import { createResolver } from './resolver'
import { css, defineTest, js, json, scss, Storage, TestUtils } from './testing'
+import { normalizePath } from './utils'
let settings: Settings = {
tailwindCSS: {
@@ -29,12 +30,14 @@ function testFixture(fixture: string, details: any[]) {
let detail = details[i]
- let configPath = path.relative(fixturePath, project.config.path)
+ let configPath = path.posix.relative(normalizePath(fixturePath), project.config.path)
expect(configPath).toEqual(detail?.config)
if (detail?.content) {
- let expected = detail?.content.map((path) => path.replace('{URL}', fixturePath)).sort()
+ let expected = detail?.content
+ .map((path) => path.replace('{URL}', normalizePath(fixturePath)))
+ .sort()
let actual = project.documentSelector
.filter((selector) => selector.priority === 1 /** content */)
@@ -45,7 +48,9 @@ function testFixture(fixture: string, details: any[]) {
}
if (detail?.selectors) {
- let expected = detail?.selectors.map((path) => path.replace('{URL}', fixturePath)).sort()
+ let expected = detail?.selectors
+ .map((path) => path.replace('{URL}', normalizePath(fixturePath)))
+ .sort()
let actual = project.documentSelector.map((selector) => selector.pattern).sort()
@@ -364,7 +369,7 @@ async function prepare({ root }: TestUtils) {
} as Settings
function adjustPath(filepath: string) {
- filepath = filepath.replace(root, '{URL}')
+ filepath = filepath.replace(normalizePath(root), '{URL}')
if (filepath.startsWith('{URL}/')) {
filepath = filepath.slice(5)
diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts
index a0516d9a..91a07b81 100644
--- a/packages/tailwindcss-language-server/src/testing/index.ts
+++ b/packages/tailwindcss-language-server/src/testing/index.ts
@@ -1,4 +1,5 @@
import { onTestFinished, test, TestOptions } from 'vitest'
+import * as os from 'node:os'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as proc from 'node:child_process'
@@ -12,6 +13,7 @@ export interface TestUtils {
export interface StorageSymlink {
[IS_A_SYMLINK]: true
filepath: string
+ type: 'file' | 'dir' | undefined
}
export interface Storage {
@@ -56,7 +58,13 @@ async function setup(config: TestConfig): Promise {
onTestFinished(async (result) => {
// Once done, move all the files to a new location
- await fs.rename(baseDir, doneDir)
+ try {
+ await fs.rename(baseDir, doneDir)
+ } catch {
+ // If it fails it doesn't really matter. It only fails on Windows and then
+ // only randomly so whatever
+ console.error('Failed to move test files to done directory')
+ }
if (result.state === 'fail') return
@@ -75,10 +83,11 @@ async function setup(config: TestConfig): Promise {
}
const IS_A_SYMLINK = Symbol('is-a-symlink')
-export function symlinkTo(filepath: string): StorageSymlink {
+export function symlinkTo(filepath: string, type?: 'file' | 'dir'): StorageSymlink {
return {
[IS_A_SYMLINK]: true as const,
filepath,
+ type,
}
}
@@ -93,7 +102,14 @@ async function prepareFileSystem(base: string, storage: Storage) {
if (typeof content === 'object' && IS_A_SYMLINK in content) {
let target = path.resolve(base, content.filepath)
- await fs.symlink(target, fullPath)
+
+ let type: string = content.type
+
+ if (os.platform() === 'win32' && content.type === 'dir') {
+ type = 'junction'
+ }
+
+ await fs.symlink(target, fullPath, type)
continue
}
diff --git a/packages/tailwindcss-language-server/tests/document-links/document-links.test.js b/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
index 0fc76cb6..f187cc46 100644
--- a/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
+++ b/packages/tailwindcss-language-server/tests/document-links/document-links.test.js
@@ -1,6 +1,7 @@
import { test } from 'vitest'
-import { withFixture } from '../common'
import * as path from 'path'
+import { URI } from 'vscode-uri'
+import { withFixture } from '../common'
withFixture('basic', (c) => {
async function testDocumentLinks(name, { text, lang, expected }) {
@@ -19,9 +20,7 @@ withFixture('basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/basic/tailwind.config.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/basic/tailwind.config.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 28 } },
},
],
@@ -32,9 +31,7 @@ withFixture('basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/basic/does-not-exist.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/basic/does-not-exist.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 27 } },
},
],
@@ -58,9 +55,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/tailwind.config.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/tailwind.config.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 28 } },
},
],
@@ -71,9 +66,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/does-not-exist.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/does-not-exist.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 27 } },
},
],
@@ -84,9 +77,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/plugin.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/plugin.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 19 } },
},
],
@@ -97,9 +88,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/does-not-exist.js')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/does-not-exist.js')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 27 } },
},
],
@@ -110,9 +99,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/index.html')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/index.html')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 20 } },
},
],
@@ -123,9 +110,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/does-not-exist.html')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/does-not-exist.html')).toString(),
range: { start: { line: 0, character: 8 }, end: { line: 0, character: 29 } },
},
],
@@ -136,9 +121,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/index.html')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/index.html')).toString(),
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 24 } },
},
],
@@ -149,9 +132,7 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path
- .resolve('./tests/fixtures/v4/basic/does-not-exist.html')
- .replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures/v4/basic/does-not-exist.html')).toString(),
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 33 } },
},
],
@@ -177,11 +158,11 @@ withFixture('v4/basic', (c) => {
lang: 'css',
expected: [
{
- target: `file://${path.resolve('./tests/fixtures').replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures')).toString(),
range: { start: { line: 1, character: 35 }, end: { line: 1, character: 43 } },
},
{
- target: `file://${path.resolve('./tests/fixtures').replace(/@/g, '%40')}`,
+ target: URI.file(path.resolve('./tests/fixtures')).toString(),
range: { start: { line: 2, character: 33 }, end: { line: 2, character: 41 } },
},
],
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 36639f55..f57a45dc 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -737,7 +737,7 @@ defineTest({
presets: [require('some-pkg/config/tailwind.config.js').default]
}
`,
- 'packages/some-pkg': symlinkTo('packages/some-pkg#c3f1e'),
+ 'packages/some-pkg': symlinkTo('packages/some-pkg#c3f1e', 'dir'),
'packages/some-pkg#c3f1e/package.json': json`
{
"name": "some-pkg",
From 45f024a2a238c82231090bbabe9cda47ec12598b Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 13:00:59 -0400
Subject: [PATCH 034/108] Update bundled CSS language service (#1286)
Fixes #1282
---
.github/workflows/ci.yml | 1 +
.../tailwindcss-language-server/package.json | 4 +--
.../tests/env/v4.test.js | 5 +++
.../tailwindcss-language-service/package.json | 2 +-
pnpm-lock.yaml | 36 +++++++++----------
5 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f64cfa03..af79a008 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,6 +7,7 @@ on:
jobs:
tests:
strategy:
+ fail-fast: false
matrix:
node: [18, 20, 22, 23]
os: [ubuntu-latest, macos-latest, windows-latest]
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index d31549e7..8059ce0d 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -90,12 +90,12 @@
"typescript": "5.3.3",
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^3.0.9",
- "vscode-css-languageservice": "6.2.9",
+ "vscode-css-languageservice": "6.3.3",
"vscode-jsonrpc": "8.2.0",
"vscode-languageclient": "8.1.0",
"vscode-languageserver": "8.1.0",
"vscode-languageserver-protocol": "^3.17.5",
- "vscode-languageserver-textdocument": "1.0.11",
+ "vscode-languageserver-textdocument": "1.0.12",
"vscode-uri": "3.0.2"
},
"engines": {
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index f57a45dc..2a6af3da 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -650,6 +650,11 @@ defineTest({
})
defineTest({
+ // This test sometimes takes a really long time on Windows because… Windows.
+ options: {
+ timeout: 30_000,
+ },
+
name: 'v3: Presets with a `#` in the name are loadable',
fs: {
'package.json': json`
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 40ddba77..c77a08c7 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -37,7 +37,7 @@
"tmp-cache": "1.1.0",
"vscode-emmet-helper-bundled": "0.0.1",
"vscode-languageserver": "8.1.0",
- "vscode-languageserver-textdocument": "1.0.11"
+ "vscode-languageserver-textdocument": "1.0.12"
},
"devDependencies": {
"@types/braces": "3.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ab264604..071263e7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -201,8 +201,8 @@ importers:
specifier: ^3.0.9
version: 3.0.9(@types/node@18.19.43)
vscode-css-languageservice:
- specifier: 6.2.9
- version: 6.2.9
+ specifier: 6.3.3
+ version: 6.3.3
vscode-jsonrpc:
specifier: 8.2.0
version: 8.2.0
@@ -216,8 +216,8 @@ importers:
specifier: ^3.17.5
version: 3.17.5
vscode-languageserver-textdocument:
- specifier: 1.0.11
- version: 1.0.11
+ specifier: 1.0.12
+ version: 1.0.12
vscode-uri:
specifier: 3.0.2
version: 3.0.2
@@ -297,8 +297,8 @@ importers:
specifier: 8.1.0
version: 8.1.0
vscode-languageserver-textdocument:
- specifier: 1.0.11
- version: 1.0.11
+ specifier: 1.0.12
+ version: 1.0.12
devDependencies:
'@types/braces':
specifier: 3.0.1
@@ -1070,8 +1070,8 @@ packages:
'@vitest/utils@3.0.9':
resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==}
- '@vscode/l10n@0.0.16':
- resolution: {integrity: sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==}
+ '@vscode/l10n@0.0.18':
+ resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==}
'@vscode/vsce@2.21.1':
resolution: {integrity: sha512-f45/aT+HTubfCU2oC7IaWnH9NjOWp668ML002QiFObFRVUCoLtcwepp9mmql/ArFUy+HCHp54Xrq4koTcOD6TA==}
@@ -2654,8 +2654,8 @@ packages:
jsdom:
optional: true
- vscode-css-languageservice@6.2.9:
- resolution: {integrity: sha512-9MsOvAi+VycKomQ7KEq4o/hLtjHHrtRLLl8lM9nMcH8cxfNI7/6jVXmsV/7pdbDWu9L3DZhsspN1eMXZwiOymw==}
+ vscode-css-languageservice@6.3.3:
+ resolution: {integrity: sha512-xXa+ftMPv6JxRgzkvPwZuDCafIdwDW3kyijGcfij1a2qBVScr2qli6MfgJzYm/AMYdbHq9I/4hdpKV0Thim2EA==}
vscode-emmet-helper-bundled@0.0.1:
resolution: {integrity: sha512-EhZ0Wt8MbdrKF3NUMfaUDhFPTdRnl1tyqYS7KOcNtsSNTV285IV+XPDtNQyw5rwYsULEfb6n+fK1DRufJQlPYw==}
@@ -2689,8 +2689,8 @@ packages:
vscode-languageserver-protocol@3.17.5:
resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
- vscode-languageserver-textdocument@1.0.11:
- resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==}
+ vscode-languageserver-textdocument@1.0.12:
+ resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
vscode-languageserver-types@3.17.2:
resolution: {integrity: sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==}
@@ -3260,7 +3260,7 @@ snapshots:
loupe: 3.1.3
tinyrainbow: 2.0.0
- '@vscode/l10n@0.0.16': {}
+ '@vscode/l10n@0.0.18': {}
'@vscode/vsce@2.21.1':
dependencies:
@@ -4900,11 +4900,11 @@ snapshots:
- supports-color
- terser
- vscode-css-languageservice@6.2.9:
+ vscode-css-languageservice@6.3.3:
dependencies:
- '@vscode/l10n': 0.0.16
- vscode-languageserver-textdocument: 1.0.11
- vscode-languageserver-types: 3.17.3
+ '@vscode/l10n': 0.0.18
+ vscode-languageserver-textdocument: 1.0.12
+ vscode-languageserver-types: 3.17.5
vscode-uri: 3.0.8
vscode-emmet-helper-bundled@0.0.1: {}
@@ -4942,7 +4942,7 @@ snapshots:
vscode-jsonrpc: 8.2.0
vscode-languageserver-types: 3.17.5
- vscode-languageserver-textdocument@1.0.11: {}
+ vscode-languageserver-textdocument@1.0.12: {}
vscode-languageserver-types@3.17.2: {}
From a8418d39a46845accdfcce4014aca9c843c0a0a1 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 13:30:08 -0400
Subject: [PATCH 035/108] Fix test timeout
Looks like I added this to the wrong test
---
.../tailwindcss-language-server/tests/env/v4.test.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 2a6af3da..01710786 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -650,11 +650,6 @@ defineTest({
})
defineTest({
- // This test sometimes takes a really long time on Windows because… Windows.
- options: {
- timeout: 30_000,
- },
-
name: 'v3: Presets with a `#` in the name are loadable',
fs: {
'package.json': json`
@@ -712,6 +707,11 @@ defineTest({
})
defineTest({
+ // This test sometimes takes a really long time on Windows because… Windows.
+ options: {
+ timeout: 30_000,
+ },
+
// This test *always* passes inside Vitest because our custom version of
// `Module._resolveFilename` is not called. Our custom implementation is
// using enhanced-resolve under the hood which is affected by the `#`
From c6a602ababe4a693f7248c90b53d14a3b1d22694 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 14:44:31 -0400
Subject: [PATCH 036/108] Make Windows tests more stable
Windows just takes way too long sometimes
---
.../tests/env/v4.test.js | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 01710786..632eb1a2 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -605,6 +605,12 @@ defineTest({
})
defineTest({
+ // This test sometimes takes a really long time on Windows because… Windows.
+ options: {
+ retry: 3,
+ timeout: 30_000,
+ },
+
name: 'Plugins with a `#` in the name are loadable',
fs: {
'app.css': css`
@@ -650,6 +656,12 @@ defineTest({
})
defineTest({
+ // This test sometimes takes a really long time on Windows because… Windows.
+ options: {
+ retry: 3,
+ timeout: 30_000,
+ },
+
name: 'v3: Presets with a `#` in the name are loadable',
fs: {
'package.json': json`
@@ -709,6 +721,7 @@ defineTest({
defineTest({
// This test sometimes takes a really long time on Windows because… Windows.
options: {
+ retry: 3,
timeout: 30_000,
},
From 5f74714b61b4f14838400761deeb50e6f1eccdee Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 15:10:24 -0400
Subject: [PATCH 037/108] Prep for new Oxide API in v4.1 (#1284)
---
.../tailwindcss-language-server/src/oxide.ts | 87 +++++--
.../src/project-locator.test.ts | 167 ++++++++++----
.../src/project-locator.ts | 7 +-
.../v4/auto-content-split/package-lock.json | 217 ------------------
.../v4/auto-content-split/package.json | 6 -
.../v4/auto-content-split/src/app.css | 3 -
.../src/components/example.html | 1 -
.../v4/auto-content-split/src/index.html | 1 -
.../v4/auto-content/package-lock.json | 217 ------------------
.../fixtures/v4/auto-content/package.json | 6 -
.../fixtures/v4/auto-content/src/app.css | 1 -
.../auto-content/src/components/example.html | 1 -
.../fixtures/v4/auto-content/src/index.html | 1 -
.../fixtures/v4/custom-source/admin/app.css | 2 -
.../fixtures/v4/custom-source/admin/foo.bin | 1 -
.../fixtures/v4/custom-source/admin/tw.css | 2 -
.../fixtures/v4/custom-source/admin/ui.css | 3 -
.../v4/custom-source/package-lock.json | 18 --
.../fixtures/v4/custom-source/package.json | 5 -
.../fixtures/v4/custom-source/shared.html | 1 -
.../fixtures/v4/custom-source/web/app.css | 2 -
.../fixtures/v4/custom-source/web/bar.bin | 1 -
22 files changed, 191 insertions(+), 559 deletions(-)
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/app.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/components/example.html
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/index.html
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/app.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/components/example.html
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/index.html
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/app.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/foo.bin
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/tw.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/ui.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/shared.html
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/app.css
delete mode 100644 packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/bar.bin
diff --git a/packages/tailwindcss-language-server/src/oxide.ts b/packages/tailwindcss-language-server/src/oxide.ts
index 5dad7553..4dd529df 100644
--- a/packages/tailwindcss-language-server/src/oxide.ts
+++ b/packages/tailwindcss-language-server/src/oxide.ts
@@ -36,8 +36,8 @@ declare namespace OxideV2 {
}
}
-// This covers the Oxide API from v4.0.0-alpha.20+
-declare namespace OxideV3 {
+// This covers the Oxide API from v4.0.0-alpha.30+
+declare namespace OxideV3And4 {
interface GlobEntry {
base: string
pattern: string
@@ -58,10 +58,37 @@ declare namespace OxideV3 {
}
}
+// This covers the Oxide API from v4.1.0+
+declare namespace OxideV5 {
+ interface GlobEntry {
+ base: string
+ pattern: string
+ }
+
+ interface SourceEntry {
+ base: string
+ pattern: string
+ negated: boolean
+ }
+
+ interface ScannerOptions {
+ sources: Array
+ }
+
+ interface ScannerConstructor {
+ new (options: ScannerOptions): Scanner
+ }
+
+ interface Scanner {
+ get files(): Array
+ get globs(): Array
+ }
+}
+
interface Oxide {
scanDir?(options: OxideV1.ScanOptions): OxideV1.ScanResult
scanDir?(options: OxideV2.ScanOptions): OxideV2.ScanResult
- Scanner?: OxideV3.ScannerConstructor
+ Scanner?: OxideV3And4.ScannerConstructor | OxideV5.ScannerConstructor
}
async function loadOxideAtPath(id: string): Promise {
@@ -78,11 +105,17 @@ interface GlobEntry {
pattern: string
}
+interface SourceEntry {
+ base: string
+ pattern: string
+ negated: boolean
+}
+
interface ScanOptions {
oxidePath: string
oxideVersion: string
basePath: string
- sources: Array
+ sources: Array
}
interface ScanResult {
@@ -118,38 +151,58 @@ export async function scan(options: ScanOptions): Promise {
}
// V2
- if (lte(options.oxideVersion, '4.0.0-alpha.19')) {
+ else if (lte(options.oxideVersion, '4.0.0-alpha.19')) {
let result = oxide.scanDir({
base: options.basePath,
- sources: options.sources,
+ sources: options.sources.map((g) => ({ base: g.base, pattern: g.pattern })),
})
return {
files: result.files,
- globs: result.globs,
+ globs: result.globs.map((g) => ({ base: g.base, pattern: g.pattern })),
}
}
// V3
- if (lte(options.oxideVersion, '4.0.0-alpha.30')) {
- let scanner = new oxide.Scanner({
+ else if (lte(options.oxideVersion, '4.0.0-alpha.30')) {
+ let scanner = new (oxide.Scanner as OxideV3And4.ScannerConstructor)({
detectSources: { base: options.basePath },
- sources: options.sources,
+ sources: options.sources.map((g) => ({ base: g.base, pattern: g.pattern })),
})
return {
files: scanner.files,
- globs: scanner.globs,
+ globs: scanner.globs.map((g) => ({ base: g.base, pattern: g.pattern })),
}
}
// V4
- let scanner = new oxide.Scanner({
- sources: [{ base: options.basePath, pattern: '**/*' }, ...options.sources],
- })
+ else if (lte(options.oxideVersion, '4.0.9999')) {
+ let scanner = new (oxide.Scanner as OxideV3And4.ScannerConstructor)({
+ sources: [
+ { base: options.basePath, pattern: '**/*' },
+ ...options.sources.map((g) => ({ base: g.base, pattern: g.pattern })),
+ ],
+ })
- return {
- files: scanner.files,
- globs: scanner.globs,
+ return {
+ files: scanner.files,
+ globs: scanner.globs.map((g) => ({ base: g.base, pattern: g.pattern })),
+ }
+ }
+
+ // V5
+ else {
+ let scanner = new (oxide.Scanner as OxideV5.ScannerConstructor)({
+ sources: [
+ { base: options.basePath, pattern: '**/*', negated: false },
+ ...options.sources.map((g) => ({ base: g.base, pattern: g.pattern, negated: g.negated })),
+ ],
+ })
+
+ return {
+ files: scanner.files,
+ globs: scanner.globs.map((g) => ({ base: g.base, pattern: g.pattern })),
+ }
}
}
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index 52af6446..ae3598e5 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -4,7 +4,7 @@ import { ProjectLocator } from './project-locator'
import { URL, fileURLToPath } from 'url'
import { Settings } from '@tailwindcss/language-service/src/util/state'
import { createResolver } from './resolver'
-import { css, defineTest, js, json, scss, Storage, TestUtils } from './testing'
+import { css, defineTest, html, js, json, scss, Storage, symlinkTo, TestUtils } from './testing'
import { normalizePath } from './utils'
let settings: Settings = {
@@ -141,58 +141,129 @@ testFixture('v4/workspaces', [
},
])
-testFixture('v4/auto-content', [
- //
- {
- config: 'src/app.css',
- content: [
- '{URL}/*',
- '{URL}/package.json',
- '{URL}/src/index.html',
- '{URL}/src/components/example.html',
- '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
- ],
+testLocator({
+ name: 'automatic content detection with Oxide',
+ fs: {
+ 'package.json': json`
+ {
+ "dependencies": {
+ "tailwindcss": "^4.0.15",
+ "@tailwindcss/oxide": "^4.0.15"
+ }
+ }
+ `,
+ 'src/index.html': html`Test
`,
+ 'src/app.css': css`
+ @import 'tailwindcss';
+ `,
+ 'src/components/example.html': html`Test
`,
},
-])
+ expected: [
+ {
+ config: '/src/app.css',
+ content: [
+ '/*',
+ '/package.json',
+ '/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/src/components/example.html',
+ '/src/index.html',
+ ],
+ },
+ ],
+})
-testFixture('v4/auto-content-split', [
- {
- config: 'src/app.css',
- content: [
- '{URL}/*',
- '{URL}/package.json',
- '{URL}/src/index.html',
- '{URL}/src/components/example.html',
- '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
- ],
+testLocator({
+ name: 'automatic content detection with Oxide using split config',
+ fs: {
+ 'package.json': json`
+ {
+ "dependencies": {
+ "tailwindcss": "^4.0.15",
+ "@tailwindcss/oxide": "^4.0.15"
+ }
+ }
+ `,
+ 'src/index.html': html`Test
`,
+ 'src/app.css': css`
+ @import 'tailwindcss/preflight' layer(base);
+ @import 'tailwindcss/theme' layer(theme);
+ @import 'tailwindcss/utilities' layer(utilities);
+ `,
+ 'src/components/example.html': html`Test
`,
},
-])
+ expected: [
+ {
+ config: '/src/app.css',
+ content: [
+ '/*',
+ '/package.json',
+ '/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/src/components/example.html',
+ '/src/index.html',
+ ],
+ },
+ ],
+})
-testFixture('v4/custom-source', [
- //
- {
- config: 'admin/app.css',
- content: [
- '{URL}/*',
- '{URL}/admin/foo.bin',
- '{URL}/admin/{**/*.bin,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
- '{URL}/package.json',
- '{URL}/shared.html',
- '{URL}/web/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
- ],
- },
- {
- config: 'web/app.css',
- content: [
- '{URL}/*',
- '{URL}/admin/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
- '{URL}/package.json',
- '{URL}/shared.html',
- '{URL}/web/bar.bin',
- '{URL}/web/{**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue},*.bin}',
- ],
+testLocator({
+ name: 'automatic content detection with custom sources',
+ fs: {
+ 'package.json': json`
+ {
+ "dependencies": {
+ "tailwindcss": "^4.0.15",
+ "@tailwindcss/oxide": "^4.0.15"
+ }
+ }
+ `,
+ 'admin/app.css': css`
+ @import './tw.css';
+ @import './ui.css';
+ `,
+ 'admin/tw.css': css`
+ @import 'tailwindcss';
+ @source './**/*.bin';
+ `,
+ 'admin/ui.css': css`
+ @theme {
+ --color-potato: #907a70;
+ }
+ `,
+ 'admin/foo.bin': html`Admin
`,
+
+ 'web/app.css': css`
+ @import 'tailwindcss';
+ @source './*.bin';
+ `,
+ 'web/bar.bin': html`Web
`,
+
+ 'shared.html': html`I belong to no one!
`,
},
-])
+ expected: [
+ {
+ config: '/admin/app.css',
+ content: [
+ '/*',
+ '/admin/foo.bin',
+ '/admin/{**/*.bin,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
+ '/package.json',
+ '/shared.html',
+ '/web/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ ],
+ },
+ {
+ config: '/web/app.css',
+ content: [
+ '/*',
+ '/admin/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/package.json',
+ '/shared.html',
+ '/web/bar.bin',
+ '/web/{**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue},*.bin}',
+ ],
+ },
+ ],
+})
testFixture('v4/missing-files', [
//
diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts
index 460b1423..c75ae1de 100644
--- a/packages/tailwindcss-language-server/src/project-locator.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.ts
@@ -627,12 +627,9 @@ async function* detectContentFiles(
resolver: Resolver,
): AsyncIterable {
try {
- let oxidePath = await resolver.resolveJsId('@tailwindcss/oxide', path.dirname(base))
+ let oxidePath = await resolver.resolveJsId('@tailwindcss/oxide', base)
oxidePath = pathToFileURL(oxidePath).href
- let oxidePackageJsonPath = await resolver.resolveJsId(
- '@tailwindcss/oxide/package.json',
- path.dirname(base),
- )
+ let oxidePackageJsonPath = await resolver.resolveJsId('@tailwindcss/oxide/package.json', base)
let oxidePackageJson = JSON.parse(await fs.readFile(oxidePackageJsonPath, 'utf8'))
let result = await oxide.scan({
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
deleted file mode 100644
index b829f613..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package-lock.json
+++ /dev/null
@@ -1,217 +0,0 @@
-{
- "name": "auto-content-split",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "dependencies": {
- "@tailwindcss/oxide": "^4.0.15",
- "tailwindcss": "^4.0.15"
- }
- },
- "node_modules/@tailwindcss/oxide": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.15.tgz",
- "integrity": "sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==",
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- },
- "optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.0.15",
- "@tailwindcss/oxide-darwin-arm64": "4.0.15",
- "@tailwindcss/oxide-darwin-x64": "4.0.15",
- "@tailwindcss/oxide-freebsd-x64": "4.0.15",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.15",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.0.15",
- "@tailwindcss/oxide-linux-arm64-musl": "4.0.15",
- "@tailwindcss/oxide-linux-x64-gnu": "4.0.15",
- "@tailwindcss/oxide-linux-x64-musl": "4.0.15",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.0.15",
- "@tailwindcss/oxide-win32-x64-msvc": "4.0.15"
- }
- },
- "node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.15.tgz",
- "integrity": "sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.15.tgz",
- "integrity": "sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.15.tgz",
- "integrity": "sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.15.tgz",
- "integrity": "sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.15.tgz",
- "integrity": "sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.15.tgz",
- "integrity": "sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.15.tgz",
- "integrity": "sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.15.tgz",
- "integrity": "sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.15.tgz",
- "integrity": "sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.15.tgz",
- "integrity": "sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.15.tgz",
- "integrity": "sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
- "license": "MIT"
- }
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
deleted file mode 100644
index 908f1d2f..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "dependencies": {
- "tailwindcss": "^4.0.15",
- "@tailwindcss/oxide": "^4.0.15"
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/app.css b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/app.css
deleted file mode 100644
index 7b4dcc31..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/app.css
+++ /dev/null
@@ -1,3 +0,0 @@
-@import "tailwindcss/preflight" layer(base);
-@import "tailwindcss/theme" layer(theme);
-@import "tailwindcss/utilities" layer(utilities);
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/components/example.html b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/components/example.html
deleted file mode 100644
index 788b45c9..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/components/example.html
+++ /dev/null
@@ -1 +0,0 @@
-Test
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/index.html b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/index.html
deleted file mode 100644
index a108a449..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content-split/src/index.html
+++ /dev/null
@@ -1 +0,0 @@
-Test
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
deleted file mode 100644
index 6b32300e..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package-lock.json
+++ /dev/null
@@ -1,217 +0,0 @@
-{
- "name": "auto-content",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "dependencies": {
- "@tailwindcss/oxide": "^4.0.15",
- "tailwindcss": "^4.0.15"
- }
- },
- "node_modules/@tailwindcss/oxide": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.15.tgz",
- "integrity": "sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==",
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- },
- "optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.0.15",
- "@tailwindcss/oxide-darwin-arm64": "4.0.15",
- "@tailwindcss/oxide-darwin-x64": "4.0.15",
- "@tailwindcss/oxide-freebsd-x64": "4.0.15",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.15",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.0.15",
- "@tailwindcss/oxide-linux-arm64-musl": "4.0.15",
- "@tailwindcss/oxide-linux-x64-gnu": "4.0.15",
- "@tailwindcss/oxide-linux-x64-musl": "4.0.15",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.0.15",
- "@tailwindcss/oxide-win32-x64-msvc": "4.0.15"
- }
- },
- "node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.15.tgz",
- "integrity": "sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.15.tgz",
- "integrity": "sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.15.tgz",
- "integrity": "sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.15.tgz",
- "integrity": "sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.15.tgz",
- "integrity": "sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.15.tgz",
- "integrity": "sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.15.tgz",
- "integrity": "sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.15.tgz",
- "integrity": "sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.15.tgz",
- "integrity": "sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.15.tgz",
- "integrity": "sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.15.tgz",
- "integrity": "sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
- "license": "MIT"
- }
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
deleted file mode 100644
index 908f1d2f..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "dependencies": {
- "tailwindcss": "^4.0.15",
- "@tailwindcss/oxide": "^4.0.15"
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/app.css b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/app.css
deleted file mode 100644
index d4b50785..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/app.css
+++ /dev/null
@@ -1 +0,0 @@
-@import 'tailwindcss';
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/components/example.html b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/components/example.html
deleted file mode 100644
index 788b45c9..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/components/example.html
+++ /dev/null
@@ -1 +0,0 @@
-Test
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/index.html b/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/index.html
deleted file mode 100644
index a108a449..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/auto-content/src/index.html
+++ /dev/null
@@ -1 +0,0 @@
-Test
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/app.css b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/app.css
deleted file mode 100644
index 41dcb5f8..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/app.css
+++ /dev/null
@@ -1,2 +0,0 @@
-@import './tw.css';
-@import './ui.css';
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/foo.bin b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/foo.bin
deleted file mode 100644
index ec56c434..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/foo.bin
+++ /dev/null
@@ -1 +0,0 @@
-Admin
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/tw.css b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/tw.css
deleted file mode 100644
index 9c0a7919..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/tw.css
+++ /dev/null
@@ -1,2 +0,0 @@
-@import 'tailwindcss';
-@source './**/*.bin';
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/ui.css b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/ui.css
deleted file mode 100644
index 24ea64d2..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/admin/ui.css
+++ /dev/null
@@ -1,3 +0,0 @@
-@theme {
- --color-potato: #907a70;
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
deleted file mode 100644
index ce004f1d..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package-lock.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "custom-source",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "dependencies": {
- "tailwindcss": "^4.0.15"
- }
- },
- "node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
- "license": "MIT"
- }
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
deleted file mode 100644
index b6cb53b1..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "dependencies": {
- "tailwindcss": "^4.0.15"
- }
-}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/shared.html b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/shared.html
deleted file mode 100644
index 49b293bf..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/shared.html
+++ /dev/null
@@ -1 +0,0 @@
-I belong to no one!
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/app.css b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/app.css
deleted file mode 100644
index 9357eb02..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/app.css
+++ /dev/null
@@ -1,2 +0,0 @@
-@import 'tailwindcss';
-@source './*.bin';
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/bar.bin b/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/bar.bin
deleted file mode 100644
index 56ee61a2..00000000
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/custom-source/web/bar.bin
+++ /dev/null
@@ -1 +0,0 @@
-Web
From 91970d58ef27f7e260ce4ceea10a9475bdc96919 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 15:44:27 -0400
Subject: [PATCH 038/108] Handle negated sources during project discovery
(#1288)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
I forgot to add support for reading `@source not` in #1284
Also realized I forgot to add the `negated` field for `@source` globs.
I'd already written the test — just not tested it with the new version
like I did the others 🤦♂️
---
.../src/css/extract-source-directives.ts | 13 +++++--
.../src/project-locator.test.ts | 34 +++++++++++++++++++
.../src/project-locator.ts | 14 +++++---
3 files changed, 55 insertions(+), 6 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/css/extract-source-directives.ts b/packages/tailwindcss-language-server/src/css/extract-source-directives.ts
index 9de33a9b..a97e3559 100644
--- a/packages/tailwindcss-language-server/src/css/extract-source-directives.ts
+++ b/packages/tailwindcss-language-server/src/css/extract-source-directives.ts
@@ -1,12 +1,21 @@
import type { Plugin } from 'postcss'
+import type { SourcePattern } from '../project-locator'
-export function extractSourceDirectives(sources: string[]): Plugin {
+export function extractSourceDirectives(sources: SourcePattern[]): Plugin {
return {
postcssPlugin: 'extract-at-rules',
AtRule: {
source: ({ params }) => {
+ let negated = /^not\s+/.test(params)
+
+ if (negated) params = params.slice(4).trimStart()
+
if (params[0] !== '"' && params[0] !== "'") return
- sources.push(params.slice(1, -1))
+
+ sources.push({
+ pattern: params.slice(1, -1),
+ negated,
+ })
},
},
}
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index ae3598e5..429f0b27 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -265,6 +265,40 @@ testLocator({
],
})
+testLocator({
+ // TODO: Enable once v4.1 is released
+ options: { skip: true },
+ name: 'automatic content detection with negative custom sources',
+ fs: {
+ 'package.json': json`
+ {
+ "dependencies": {
+ "tailwindcss": "0.0.0-insiders.3e53e25",
+ "@tailwindcss/oxide": "0.0.0-insiders.3e53e25"
+ }
+ }
+ `,
+ 'src/app.css': css`
+ @import 'tailwindcss';
+ @source './**/*.html';
+ @source not './ignored.html';
+ `,
+ 'src/index.html': html`
`,
+ 'src/ignored.html': html`
`,
+ },
+ expected: [
+ {
+ config: '/src/app.css',
+ content: [
+ '/*',
+ '/package.json',
+ '/src/index.html',
+ '/src/{**/*.html,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
+ ],
+ },
+ ],
+})
+
testFixture('v4/missing-files', [
//
{
diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts
index c75ae1de..c04738bf 100644
--- a/packages/tailwindcss-language-server/src/project-locator.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.ts
@@ -623,7 +623,7 @@ async function* contentSelectorsFromCssConfig(
async function* detectContentFiles(
base: string,
inputFile: string,
- sources: string[],
+ sources: SourcePattern[],
resolver: Resolver,
): AsyncIterable {
try {
@@ -636,9 +636,10 @@ async function* detectContentFiles(
oxidePath,
oxideVersion: oxidePackageJson.version,
basePath: base,
- sources: sources.map((pattern) => ({
+ sources: sources.map((source) => ({
base: path.dirname(inputFile),
- pattern,
+ pattern: source.pattern,
+ negated: source.negated,
})),
})
@@ -672,11 +673,16 @@ type ConfigEntry = {
content: ContentItem[]
}
+export interface SourcePattern {
+ pattern: string
+ negated: boolean
+}
+
class FileEntry {
content: string | null
deps: FileEntry[] = []
realpath: string | null
- sources: string[] = []
+ sources: SourcePattern[] = []
meta: TailwindStylesheet | null = null
constructor(
From 285d70d3b2aac6f9251f9eb85a48f8e3c0948eae Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 27 Mar 2025 15:45:54 -0400
Subject: [PATCH 039/108] Update changelog
---
packages/vscode-tailwindcss/CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index de81de55..9a2ce434 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,8 @@
- Fix content detection when using v4.0+ ([#1280](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1280))
- Ensure file exclusions always work on Windows ([#1281](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1281))
+- Prep for new Oxide API in v4.1 ([#1284](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1284))
+- Handle negated sources during project discovery in v4.1 ([#1288](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1288))
# 0.14.11
From 3542626c74049e200b4ce9ed035a89864da95a17 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Fri, 28 Mar 2025 16:37:10 -0400
Subject: [PATCH 040/108] 0.14.12
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 8059ce0d..dd812e3c 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.11",
+ "version": "0.14.12",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index c77a08c7..48fae16e 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.11",
+ "version": "0.14.12",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 9a2ce434..31a8ca66 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.12
+
- Fix content detection when using v4.0+ ([#1280](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1280))
- Ensure file exclusions always work on Windows ([#1281](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1281))
- Prep for new Oxide API in v4.1 ([#1284](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1284))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 038c9133..26af65d3 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.11",
+ "version": "0.14.12",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 6b306ba89b75a26bd420c61582242bb8251515df Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 1 Apr 2025 10:22:18 -0400
Subject: [PATCH 041/108] =?UTF-8?q?Hide=20completions=20from=20CSS=20langu?=
=?UTF-8?q?age=20server=20inside=20`@import=20"=E2=80=A6"=20source(?=
=?UTF-8?q?=E2=80=A6)`=20(#1091)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This hides completions from the CSS language server when inside `@import
source(…)`, `@import theme(…)`, and `@import prefix(…)`
Because of the way we fake stuff to the underlying service it thinks
these are valid places to provide standard CSS completions but they're
not.
before:
after:
---
.../src/language/css-server.ts | 56 ++++++++++++++++++-
.../tests/css/css-server.test.ts | 46 +++++++++++++++
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
3 files changed, 101 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/language/css-server.ts b/packages/tailwindcss-language-server/src/language/css-server.ts
index a43910b2..73e967fc 100644
--- a/packages/tailwindcss-language-server/src/language/css-server.ts
+++ b/packages/tailwindcss-language-server/src/language/css-server.ts
@@ -13,7 +13,7 @@ import {
CompletionItemKind,
Connection,
} from 'vscode-languageserver/node'
-import { TextDocument } from 'vscode-languageserver-textdocument'
+import { Position, TextDocument } from 'vscode-languageserver-textdocument'
import { Utils, URI } from 'vscode-uri'
import { getLanguageModelCache } from './languageModelCache'
import { Stylesheet } from 'vscode-css-languageservice'
@@ -121,6 +121,7 @@ export class CssServer {
async function withDocumentAndSettings(
uri: string,
callback: (result: {
+ original: TextDocument
document: TextDocument
settings: LanguageSettings | undefined
}) => T | Promise,
@@ -130,13 +131,64 @@ export class CssServer {
return null
}
return await callback({
+ original: document,
document: createVirtualCssDocument(document),
settings: await getDocumentSettings(document),
})
}
+ function isInImportDirective(doc: TextDocument, pos: Position) {
+ let text = doc.getText({
+ start: { line: pos.line, character: 0 },
+ end: pos,
+ })
+
+ // Scan backwards to see if we're inside an `@import` directive
+ let foundImport = false
+ let foundDirective = false
+
+ for (let i = text.length - 1; i >= 0; i--) {
+ let char = text[i]
+ if (char === '\n') break
+
+ if (char === '(' && !foundDirective) {
+ if (text.startsWith(' source(', i - 7)) {
+ foundDirective = true
+ }
+
+ //
+ else if (text.startsWith(' theme(', i - 6)) {
+ foundDirective = true
+ }
+
+ //
+ else if (text.startsWith(' prefix(', i - 7)) {
+ foundDirective = true
+ }
+ }
+
+ //
+ else if (char === '@' && !foundImport) {
+ if (text.startsWith('@import ', i)) {
+ foundImport = true
+ }
+ }
+ }
+
+ return foundImport && foundDirective
+ }
+
connection.onCompletion(async ({ textDocument, position }, _token) =>
- withDocumentAndSettings(textDocument.uri, async ({ document, settings }) => {
+ withDocumentAndSettings(textDocument.uri, async ({ original, document, settings }) => {
+ // If we're inside source(…), prefix(…), or theme(…), don't show
+ // completions from the CSS language server
+ if (isInImportDirective(original, position)) {
+ return {
+ isIncomplete: false,
+ items: [],
+ }
+ }
+
let result = await cssLanguageService.doComplete2(
document,
position,
diff --git a/packages/tailwindcss-language-server/tests/css/css-server.test.ts b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
index e8970e08..388e94af 100644
--- a/packages/tailwindcss-language-server/tests/css/css-server.test.ts
+++ b/packages/tailwindcss-language-server/tests/css/css-server.test.ts
@@ -689,3 +689,49 @@ defineTest({
expect(await doc.diagnostics()).toEqual([])
},
})
+
+defineTest({
+ name: 'completions are hidden inside @import source(…)/theme(…)/prefix(…) functions',
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ server: 'css',
+ root,
+ }),
+ }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'tailwindcss',
+ name: 'file-1.css',
+ text: css`
+ @import './file.css' source(none);
+ @import './file.css' theme(inline);
+ @import './file.css' prefix(tw);
+ @import './file.css' source(none) theme(inline) prefix(tw);
+ `,
+ })
+
+ // @import './file.css' source(none)
+ // ^
+ // @import './file.css' theme(inline);
+ // ^
+ // @import './file.css' prefix(tw);
+ // ^
+ let completionsA = await doc.completions({ line: 0, character: 29 })
+ let completionsB = await doc.completions({ line: 1, character: 28 })
+ let completionsC = await doc.completions({ line: 2, character: 29 })
+
+ expect(completionsA).toEqual({ isIncomplete: false, items: [] })
+ expect(completionsB).toEqual({ isIncomplete: false, items: [] })
+ expect(completionsC).toEqual({ isIncomplete: false, items: [] })
+
+ // @import './file.css' source(none) theme(inline) prefix(tw);
+ // ^ ^ ^
+ let completionsD = await doc.completions({ line: 3, character: 29 })
+ let completionsE = await doc.completions({ line: 3, character: 41 })
+ let completionsF = await doc.completions({ line: 3, character: 56 })
+
+ expect(completionsD).toEqual({ isIncomplete: false, items: [] })
+ expect(completionsE).toEqual({ isIncomplete: false, items: [] })
+ expect(completionsF).toEqual({ isIncomplete: false, items: [] })
+ },
+})
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 31a8ca66..82478125 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Hide completions from CSS language server inside `@import "…" source(…)` ([#1091](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1091))
# 0.14.12
From b92d170d45324501dc4541fa7f7c2f689e9cab58 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 2 Apr 2025 10:14:58 -0400
Subject: [PATCH 042/108] Bump to Tailwind CSS v4.1.1 (#1294)
---
.../tailwindcss-language-server/package.json | 4 +-
.../src/project-locator.test.ts | 52 +++++----
.../src/util/v4/design-system.ts | 8 ++
.../tests/colors/colors.test.js | 4 +-
.../tests/completions/completions.test.js | 26 ++---
.../tests/env/v4.test.js | 22 ++--
.../tests/fixtures/v4/basic/package-lock.json | 8 +-
.../tests/fixtures/v4/basic/package.json | 2 +-
.../v4/css-loading-js/package-lock.json | 8 +-
.../fixtures/v4/css-loading-js/package.json | 2 +-
.../v4/dependencies/package-lock.json | 8 +-
.../fixtures/v4/dependencies/package.json | 2 +-
.../v4/invalid-import-order/package-lock.json | 8 +-
.../v4/invalid-import-order/package.json | 2 +-
.../v4/missing-files/package-lock.json | 8 +-
.../fixtures/v4/missing-files/package.json | 2 +-
.../v4/multi-config/package-lock.json | 8 +-
.../fixtures/v4/multi-config/package.json | 2 +-
.../v4/path-mappings/package-lock.json | 8 +-
.../fixtures/v4/path-mappings/package.json | 2 +-
.../fixtures/v4/with-prefix/package-lock.json | 8 +-
.../fixtures/v4/with-prefix/package.json | 2 +-
.../fixtures/v4/workspaces/package-lock.json | 8 +-
.../tests/fixtures/v4/workspaces/package.json | 2 +-
.../tests/hover/hover.test.js | 21 ++--
.../src/util/color.ts | 30 ++++-
.../src/util/rewriting/var-fallbacks.ts | 8 ++
packages/vscode-tailwindcss/CHANGELOG.md | 2 +
pnpm-lock.yaml | 108 +++++++++---------
29 files changed, 213 insertions(+), 162 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index dd812e3c..d96255dd 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -40,7 +40,7 @@
"@tailwindcss/forms": "0.5.3",
"@tailwindcss/language-service": "workspace:*",
"@tailwindcss/line-clamp": "0.4.2",
- "@tailwindcss/oxide": "^4.0.15",
+ "@tailwindcss/oxide": "^4.1.0",
"@tailwindcss/typography": "0.5.7",
"@types/braces": "3.0.1",
"@types/color-name": "^1.1.3",
@@ -84,7 +84,7 @@
"rimraf": "3.0.2",
"stack-trace": "0.0.10",
"tailwindcss": "3.4.17",
- "tailwindcss-v4": "npm:tailwindcss@4.0.6",
+ "tailwindcss-v4": "npm:tailwindcss@4.1.1",
"tsconfck": "^3.1.4",
"tsconfig-paths": "^4.2.0",
"typescript": "5.3.3",
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index 429f0b27..3699c840 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -123,6 +123,7 @@ testFixture('v4/workspaces', [
'{URL}/packages/admin/**',
'{URL}/packages/admin/app.css',
'{URL}/packages/admin/package.json',
+ '{URL}/packages/admin/tw.css',
],
},
{
@@ -147,8 +148,8 @@ testLocator({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "^4.0.15",
- "@tailwindcss/oxide": "^4.0.15"
+ "tailwindcss": "4.1.0",
+ "@tailwindcss/oxide": "4.1.0"
}
}
`,
@@ -164,7 +165,7 @@ testLocator({
content: [
'/*',
'/package.json',
- '/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/src/**/*.{aspx,astro,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
'/src/components/example.html',
'/src/index.html',
],
@@ -178,8 +179,8 @@ testLocator({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "^4.0.15",
- "@tailwindcss/oxide": "^4.0.15"
+ "tailwindcss": "4.1.0",
+ "@tailwindcss/oxide": "4.1.0"
}
}
`,
@@ -197,7 +198,7 @@ testLocator({
content: [
'/*',
'/package.json',
- '/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/src/**/*.{aspx,astro,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
'/src/components/example.html',
'/src/index.html',
],
@@ -211,8 +212,8 @@ testLocator({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "^4.0.15",
- "@tailwindcss/oxide": "^4.0.15"
+ "tailwindcss": "4.1.0",
+ "@tailwindcss/oxide": "4.1.0"
}
}
`,
@@ -245,36 +246,40 @@ testLocator({
content: [
'/*',
'/admin/foo.bin',
- '/admin/{**/*.bin,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
+ '/admin/tw.css',
+ '/admin/ui.css',
+ '/admin/{**/*.bin,**/*.{aspx,astro,bin,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
'/package.json',
'/shared.html',
- '/web/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/web/**/*.{aspx,astro,bin,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/web/app.css',
],
},
{
config: '/web/app.css',
content: [
'/*',
- '/admin/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/admin/**/*.{aspx,astro,bin,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '/admin/app.css',
+ '/admin/tw.css',
+ '/admin/ui.css',
'/package.json',
'/shared.html',
'/web/bar.bin',
- '/web/{**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue},*.bin}',
+ '/web/{**/*.{aspx,astro,bin,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue},*.bin}',
],
},
],
})
testLocator({
- // TODO: Enable once v4.1 is released
- options: { skip: true },
name: 'automatic content detection with negative custom sources',
fs: {
'package.json': json`
{
"dependencies": {
- "tailwindcss": "0.0.0-insiders.3e53e25",
- "@tailwindcss/oxide": "0.0.0-insiders.3e53e25"
+ "tailwindcss": "4.1.0",
+ "@tailwindcss/oxide": "4.1.0"
}
}
`,
@@ -293,7 +298,7 @@ testLocator({
'/*',
'/package.json',
'/src/index.html',
- '/src/{**/*.html,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
+ '/src/{**/*.html,**/*.{aspx,astro,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}',
],
},
],
@@ -303,7 +308,7 @@ testFixture('v4/missing-files', [
//
{
config: 'app.css',
- content: ['{URL}/*', '{URL}/package.json'],
+ content: ['{URL}/*', '{URL}/i-exist.css', '{URL}/package.json'],
},
])
@@ -314,7 +319,8 @@ testFixture('v4/path-mappings', [
content: [
'{URL}/*',
'{URL}/package.json',
- '{URL}/src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '{URL}/src/**/*.{aspx,astro,cjs,css,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}',
+ '{URL}/src/a/file.css',
'{URL}/src/a/my-config.ts',
'{URL}/src/a/my-plugin.ts',
'{URL}/tsconfig.json',
@@ -326,7 +332,7 @@ testFixture('v4/invalid-import-order', [
//
{
config: 'tailwind.css',
- content: ['{URL}/*', '{URL}/package.json'],
+ content: ['{URL}/*', '{URL}/a.css', '{URL}/b.css', '{URL}/package.json'],
},
])
@@ -338,7 +344,7 @@ testLocator({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "^4.0.2"
+ "tailwindcss": "4.1.0"
}
}
`,
@@ -386,7 +392,7 @@ testLocator({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "4.0.6"
+ "tailwindcss": "4.1.0"
}
}
`,
@@ -415,7 +421,7 @@ testLocator({
},
expected: [
{
- version: '4.0.6',
+ version: '4.1.0',
config: '/src/articles/articles.css',
content: [],
},
diff --git a/packages/tailwindcss-language-server/src/util/v4/design-system.ts b/packages/tailwindcss-language-server/src/util/v4/design-system.ts
index 05d2ecd3..f312b95c 100644
--- a/packages/tailwindcss-language-server/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-server/src/util/v4/design-system.ts
@@ -219,6 +219,14 @@ export async function loadDesignSystem(
Object.assign(design, {
dependencies: () => dependencies,
+ // TODOs:
+ //
+ // 1. Remove PostCSS parsing — its roughly 60% of the processing time
+ // ex: compiling 19k classes take 650ms and 400ms of that is PostCSS
+ //
+ // - Replace `candidatesToCss` with a `candidatesToAst` API
+ // First step would be to convert to a PostCSS AST by transforming the nodes directly
+ // Then it would be to drop the PostCSS AST representation entirely in all v4 code paths
compile(classes: string[]): (postcss.Root | null)[] {
let css = design.candidatesToCss(classes)
let errors: any[] = []
diff --git a/packages/tailwindcss-language-server/tests/colors/colors.test.js b/packages/tailwindcss-language-server/tests/colors/colors.test.js
index 5016bacc..4780a4fb 100644
--- a/packages/tailwindcss-language-server/tests/colors/colors.test.js
+++ b/packages/tailwindcss-language-server/tests/colors/colors.test.js
@@ -334,7 +334,7 @@ defineTest({
expect(c.project).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
@@ -373,7 +373,7 @@ defineTest({
expect(c.project).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index dbe9a352..feb4999f 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -313,8 +313,8 @@ withFixture('v4/basic', (c) => {
let result = await completion({ lang, text, position, settings })
let textEdit = expect.objectContaining({ range: { start: position, end: position } })
- expect(result.items.length).toBe(13172)
- expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(317)
+ expect(result.items.length).toBe(19283)
+ expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(346)
expect(result).toEqual({
isIncomplete: false,
items: expect.arrayContaining([
@@ -488,7 +488,7 @@ withFixture('v4/basic', (c) => {
})
// Make sure `@slot` is NOT suggested by default
- expect(result.items.length).toBe(7)
+ expect(result.items.length).toBe(8)
expect(result.items).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ kind: 14, label: '@slot', sortText: '-0000000' }),
@@ -627,7 +627,7 @@ withFixture('v4/basic', (c) => {
expect(resolved).toEqual({
...item,
- detail: 'background-color: oklch(0.637 0.237 25.331);',
+ detail: 'background-color: oklch(63.7% 0.237 25.331);',
documentation: '#fb2c36',
})
})
@@ -692,7 +692,7 @@ defineTest({
// ^
let completion = await document.completions({ line: 0, character: 23 })
- expect(completion?.items.length).toBe(12289)
+ expect(completion?.items.length).toBe(19236)
},
})
@@ -714,7 +714,7 @@ defineTest({
// ^
let completion = await document.completions({ line: 0, character: 22 })
- expect(completion?.items.length).toBe(12289)
+ expect(completion?.items.length).toBe(19236)
},
})
@@ -736,7 +736,7 @@ defineTest({
// ^
let completion = await document.completions({ line: 0, character: 31 })
- expect(completion?.items.length).toBe(12289)
+ expect(completion?.items.length).toBe(19236)
},
})
@@ -765,7 +765,7 @@ defineTest({
// ^
let completion = await document.completions({ line: 0, character: 20 })
- expect(completion?.items.length).toBe(12289)
+ expect(completion?.items.length).toBe(19236)
},
})
@@ -796,7 +796,7 @@ defineTest({
// ^
let completion = await document.completions({ line: 1, character: 22 })
- expect(completion?.items.length).toBe(12289)
+ expect(completion?.items.length).toBe(19236)
},
})
@@ -886,24 +886,24 @@ defineTest({
// ^
let completionA = await document.completions({ line: 0, character: 13 })
- expect(completionA?.items.length).toBe(12289)
+ expect(completionA?.items.length).toBe(19236)
// return ;
// ^
let completionB = await document.completions({ line: 3, character: 30 })
- expect(completionB?.items.length).toBe(12289)
+ expect(completionB?.items.length).toBe(19236)
// return ;
// ^
let completionC = await document.completions({ line: 7, character: 30 })
- expect(completionC?.items.length).toBe(12289)
+ expect(completionC?.items.length).toBe(19236)
// let y = cva("");
// ^
let completionD = await document.completions({ line: 10, character: 13 })
- expect(completionD?.items.length).toBe(12289)
+ expect(completionD?.items.length).toBe(19236)
},
})
diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js
index 632eb1a2..dc33c79f 100644
--- a/packages/tailwindcss-language-server/tests/env/v4.test.js
+++ b/packages/tailwindcss-language-server/tests/env/v4.test.js
@@ -21,7 +21,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
@@ -49,7 +49,7 @@ defineTest({
},
})
- expect(completion?.items.length).toBe(12288)
+ expect(completion?.items.length).toBe(19235)
},
})
@@ -137,7 +137,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
@@ -188,7 +188,7 @@ defineTest({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "4.0.1"
+ "tailwindcss": "4.1.1"
}
}
`,
@@ -205,7 +205,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.1',
+ version: '4.1.1',
isDefaultVersion: false,
},
})
@@ -233,7 +233,7 @@ defineTest({
},
})
- expect(completion?.items.length).toBe(12288)
+ expect(completion?.items.length).toBe(19235)
},
})
@@ -243,7 +243,7 @@ defineTest({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "4.0.1"
+ "tailwindcss": "4.1.1"
}
}
`,
@@ -270,7 +270,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.1',
+ version: '4.1.1',
isDefaultVersion: false,
},
})
@@ -322,7 +322,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
@@ -354,7 +354,7 @@ defineTest({
'package.json': json`
{
"dependencies": {
- "tailwindcss": "4.0.1"
+ "tailwindcss": "4.1.1"
}
}
`,
@@ -831,7 +831,7 @@ defineTest({
expect(await client.project()).toMatchObject({
tailwind: {
- version: '4.0.6',
+ version: '4.1.1',
isDefaultVersion: true,
},
})
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
index 6275c3dc..ab6be977 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
index 5089dc65..b92fb848 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
index 555ee660..f4352dc6 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
index 24d978d8..0a12b281 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
index ed4d2d9a..c95b25d7 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
index c4664645..58a06cee 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
index 651bf7c9..e6cc18e9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
index 1e5486ae..34a2cc13 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json
@@ -5,13 +5,13 @@
"packages": {
"": {
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
index b6cb53b1..43b975c9 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
index b622b7b7..a88643ba 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json
@@ -8,7 +8,7 @@
"packages/*"
],
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
},
"node_modules/@private/admin": {
@@ -32,9 +32,9 @@
"link": true
},
"node_modules/tailwindcss": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
- "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
+ "integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT"
},
"packages/admin": {
diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
index aa5e54db..9291956a 100644
--- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
+++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json
@@ -3,6 +3,6 @@
"packages/*"
],
"dependencies": {
- "tailwindcss": "^4.0.15"
+ "tailwindcss": "4.1.1"
}
}
diff --git a/packages/tailwindcss-language-server/tests/hover/hover.test.js b/packages/tailwindcss-language-server/tests/hover/hover.test.js
index ac97e414..37377340 100644
--- a/packages/tailwindcss-language-server/tests/hover/hover.test.js
+++ b/packages/tailwindcss-language-server/tests/hover/hover.test.js
@@ -214,7 +214,7 @@ withFixture('v4/basic', (c) => {
text: '',
position: { line: 0, character: 13 },
expected:
- '.bg-red-500 {\n background-color: var(--color-red-500) /* oklch(0.637 0.237 25.331) = #fb2c36 */;\n}',
+ '.bg-red-500 {\n background-color: var(--color-red-500) /* oklch(63.7% 0.237 25.331) = #fb2c36 */;\n}',
expectedRange: {
start: { line: 0, character: 12 },
end: { line: 0, character: 22 },
@@ -231,16 +231,15 @@ withFixture('v4/basic', (c) => {
},
})
- test.todo('arbitrary value with theme function')
- // testHover('arbitrary value with theme function', {
- // text: '
',
- // position: { line: 0, character: 13 },
- // expected: '.p-\\[theme\\(spacing\\.4\\)\\] {\n' + ' padding: 1rem /* 16px */;\n' + '}',
- // expectedRange: {
- // start: { line: 0, character: 12 },
- // end: { line: 0, character: 32 },
- // },
- // })
+ testHover('arbitrary value with theme function', {
+ text: '
',
+ position: { line: 0, character: 13 },
+ expected: '.p-\\[theme\\(spacing\\.4\\)\\] {\n' + ' padding: 1rem /* 16px */;\n' + '}',
+ expectedRange: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 32 },
+ },
+ })
testHover('arbitrary property', {
text: '
',
diff --git a/packages/tailwindcss-language-service/src/util/color.ts b/packages/tailwindcss-language-service/src/util/color.ts
index 4b0d3b84..a1a99d66 100644
--- a/packages/tailwindcss-language-service/src/util/color.ts
+++ b/packages/tailwindcss-language-service/src/util/color.ts
@@ -57,7 +57,7 @@ const colorRegex = new RegExp(
)
function getColorsInString(state: State, str: string): (culori.Color | KeywordColor)[] {
- if (/(?:box|drop)-shadow/.test(str)) return []
+ if (/(?:box|drop)-shadow/.test(str) && !/--tw-drop-shadow/.test(str)) return []
function toColor(match: RegExpMatchArray) {
let color = match[1].replace(/var\([^)]+\)/, '1')
@@ -85,6 +85,17 @@ function getColorFromDecls(
) {
return false
}
+
+ // ignore mask-image & mask-composite
+ if (prop === 'mask-image' || prop === 'mask-composite') {
+ return false
+ }
+
+ // ignore `--tw-drop-shadow`
+ if (prop === '--tw-drop-shadow') {
+ return false
+ }
+
return true
})
@@ -177,8 +188,25 @@ function getColorFromRoot(state: State, css: postcss.Root): culori.Color | Keywo
return getColorFromDecls(state, decls)
}
+let isNegative = /^-/
+let isNumericUtility =
+ /^-?((min-|max-)?[wh]|z|start|order|opacity|rounded|row|col|size|basis|end|duration|ease|font|top|left|bottom|right|inset|leading|cursor|(space|scale|skew|rotate)-[xyz]|gap(-[xy])?|(scroll-)?[pm][trblxyse]?)-/
+let isMaskUtility = /^-?mask-/
+
+function isLikelyColorless(className: string) {
+ if (isNegative.test(className)) return true
+ // TODO: This is **not** correct but is intentional because there are 5k mask utilities and a LOT of them are colors
+ // This causes a massive slowdown when building the design system
+ if (isMaskUtility.test(className)) return true
+ if (isNumericUtility.test(className)) return true
+ return false
+}
+
export function getColor(state: State, className: string): culori.Color | KeywordColor | null {
if (state.v4) {
+ // FIXME: This is a performance optimization and not strictly correct
+ if (isLikelyColorless(className)) return null
+
let css = state.designSystem.compile([className])[0]
let color = getColorFromRoot(state, css)
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts b/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
index 728b53bf..91dcc91d 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
@@ -17,6 +17,14 @@ export function replaceCssVarsWithFallbacks(state: State, str: string): string {
return fallback
}
+ if (
+ name === '--tw-text-shadow-alpha' ||
+ name === '--tw-drop-shadow-alpha' ||
+ name === '--tw-shadow-alpha'
+ ) {
+ return '100%'
+ }
+
// Don't touch it since there's no suitable replacement
return null
},
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 82478125..421543f5 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,8 @@
## Prerelease
- Hide completions from CSS language server inside `@import "…" source(…)` ([#1091](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1091))
+- Bump bundled v4 fallback to v4.1.1 ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
+- Show color swatches for most new v4.1 utilities ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
# 0.14.12
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 071263e7..e879d3a6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,8 +51,8 @@ importers:
specifier: 0.4.2
version: 0.4.2(tailwindcss@3.4.17)
'@tailwindcss/oxide':
- specifier: ^4.0.15
- version: 4.0.15
+ specifier: ^4.1.0
+ version: 4.1.0
'@tailwindcss/typography':
specifier: 0.5.7
version: 0.5.7(tailwindcss@3.4.17)
@@ -183,8 +183,8 @@ importers:
specifier: 3.4.17
version: 3.4.17
tailwindcss-v4:
- specifier: npm:tailwindcss@4.0.6
- version: tailwindcss@4.0.6
+ specifier: npm:tailwindcss@4.1.1
+ version: tailwindcss@4.1.1
tsconfck:
specifier: ^3.1.4
version: 3.1.4(typescript@5.3.3)
@@ -890,74 +890,74 @@ packages:
peerDependencies:
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
- '@tailwindcss/oxide-android-arm64@4.0.15':
- resolution: {integrity: sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==}
+ '@tailwindcss/oxide-android-arm64@4.1.0':
+ resolution: {integrity: sha512-UredFljuHey2Kh5qyYfQVBr0Xfq70ZE5Df6i5IubNYQGs2JXXT4VL0SIUjwzHx5W9T6t7dT7banunlV6lthGPQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.0.15':
- resolution: {integrity: sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==}
+ '@tailwindcss/oxide-darwin-arm64@4.1.0':
+ resolution: {integrity: sha512-QHQ/46lRVwH9zEBNiRk8AJ3Af4pMq6DuZAI//q323qrPOXjsRdrhLsH9LUO3mqBfHr5EZNUxN3Am5vpO89sntw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.0.15':
- resolution: {integrity: sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==}
+ '@tailwindcss/oxide-darwin-x64@4.1.0':
+ resolution: {integrity: sha512-lEMgYHCvQQ6x2KOZ4FwnPprwfnc+UnjzwXRqEYIhB/NlYvXQD1QMf7oKEDRqy94DiZaYox9ZRfG2YJOBgM0UkA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.0.15':
- resolution: {integrity: sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==}
+ '@tailwindcss/oxide-freebsd-x64@4.1.0':
+ resolution: {integrity: sha512-9fdImTc+2lA5yHqJ61oeTXfCtzylNOzJVFhyWwVQAJESJJbVCPnj6f+b+Zf/AYAdKQfS6FCThbPEahkQrDCgLQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
- resolution: {integrity: sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.0':
+ resolution: {integrity: sha512-HB0bTkUOuTLLSdadyRhKE9yps4/ZBjrojbHTPMSvvf/8yBLZRPpWb+A6IgW5R+2A2AL4KhVPgLwWfoXsErxJFg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
- resolution: {integrity: sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.0':
+ resolution: {integrity: sha512-+QtYCwvKLjC46h6RikKkpELJWrpiMMtgyK0aaqhwPLEx1icGgIhwz8dqrkAiqbFRE0KiRrE2aenhYoEkplyRmA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
- resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.0':
+ resolution: {integrity: sha512-nApadFKM9GauzuPZPlt6TKfELavMHqJ0gVd+GYkYBTwr2t9KhgCAb2sKiFDDIhs1a7gOjsU7P1lEauv3iKFp+Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
- resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.0':
+ resolution: {integrity: sha512-cp0Rf9Wit2kZHhrV8HIoDFD8dxU2+ZTCFCFbDj3a07pGyyPwLCJm5H5VipKXgYrBaLmlYu73ERidW0S5sdEXEg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.0.15':
- resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==}
+ '@tailwindcss/oxide-linux-x64-musl@4.1.0':
+ resolution: {integrity: sha512-4/wf42XWBJGXsOS6BhgPhdQbg/qyfdZ1nZvTL9sJoxYN+Ah+cfY5Dd7R0smzI8hmgCRt3TD1lYb72ChTyIA59w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
- resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.0':
+ resolution: {integrity: sha512-caXJJ0G6NwGbcoxEYdH3MZYN84C3PldaMdAEPMU6bjJXURQlKdSlQ/Ecis7/nSgBkMkicZyhqWmb36Tw/BFSIw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
- resolution: {integrity: sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.0':
+ resolution: {integrity: sha512-ZHXRXRxB7HBmkUE8U13nmkGGYfR1I2vsuhiYjeDDUFIYpk1BL6caU8hvzkSlL/X5CAQNdIUUJRGom5I0ZyfJOA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.0.15':
- resolution: {integrity: sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==}
+ '@tailwindcss/oxide@4.1.0':
+ resolution: {integrity: sha512-A33oyZKpPFH08d7xkl13Dc8OTsbPhsuls0z9gUCxIHvn8c1BsUACddQxL6HwaeJR1fSYyXZUw8bdWcD8bVawpQ==}
engines: {node: '>= 10'}
'@tailwindcss/typography@0.5.7':
@@ -2440,8 +2440,8 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
- tailwindcss@4.0.6:
- resolution: {integrity: sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==}
+ tailwindcss@4.1.1:
+ resolution: {integrity: sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==}
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
@@ -3105,52 +3105,52 @@ snapshots:
dependencies:
tailwindcss: 3.4.17
- '@tailwindcss/oxide-android-arm64@4.0.15':
+ '@tailwindcss/oxide-android-arm64@4.1.0':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.0.15':
+ '@tailwindcss/oxide-darwin-arm64@4.1.0':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.0.15':
+ '@tailwindcss/oxide-darwin-x64@4.1.0':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.0.15':
+ '@tailwindcss/oxide-freebsd-x64@4.1.0':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.0':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.0':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.0':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.0':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.0.15':
+ '@tailwindcss/oxide-linux-x64-musl@4.1.0':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.0':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.0':
optional: true
- '@tailwindcss/oxide@4.0.15':
+ '@tailwindcss/oxide@4.1.0':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.0.15
- '@tailwindcss/oxide-darwin-arm64': 4.0.15
- '@tailwindcss/oxide-darwin-x64': 4.0.15
- '@tailwindcss/oxide-freebsd-x64': 4.0.15
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.15
- '@tailwindcss/oxide-linux-arm64-gnu': 4.0.15
- '@tailwindcss/oxide-linux-arm64-musl': 4.0.15
- '@tailwindcss/oxide-linux-x64-gnu': 4.0.15
- '@tailwindcss/oxide-linux-x64-musl': 4.0.15
- '@tailwindcss/oxide-win32-arm64-msvc': 4.0.15
- '@tailwindcss/oxide-win32-x64-msvc': 4.0.15
+ '@tailwindcss/oxide-android-arm64': 4.1.0
+ '@tailwindcss/oxide-darwin-arm64': 4.1.0
+ '@tailwindcss/oxide-darwin-x64': 4.1.0
+ '@tailwindcss/oxide-freebsd-x64': 4.1.0
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.0
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.0
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.0
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.0
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.0
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.0
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.0
'@tailwindcss/typography@0.5.7(tailwindcss@3.4.17)':
dependencies:
@@ -4710,7 +4710,7 @@ snapshots:
transitivePeerDependencies:
- ts-node
- tailwindcss@4.0.6: {}
+ tailwindcss@4.1.1: {}
tapable@2.2.1: {}
From 97cfd2df7474bd0bcdf705ddf5f5d82aeb8a57a8 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 2 Apr 2025 10:19:13 -0400
Subject: [PATCH 043/108] Add CSS `var()` hovers when using v4 (#1289)
This adds support for hovering over things like `var(--color-red-500)` or `var(--breakpoint-xl)` and showing the value from the theme like we already support with the `theme()` function.
Additionally, I've improved the hovers for v4 theme keys. They now appear in a `@theme` block like you'd see them in your CSS and are also syntax highlighted.
---
.../tests/hover/hover.test.js | 82 ++++++++++++++++++-
.../getInvalidConfigPathDiagnostics.ts | 5 ++
.../src/hoverProvider.ts | 17 +++-
.../src/util/find.ts | 6 +-
.../src/util/state.ts | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 2 +
6 files changed, 108 insertions(+), 6 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/hover/hover.test.js b/packages/tailwindcss-language-server/tests/hover/hover.test.js
index 37377340..379f4199 100644
--- a/packages/tailwindcss-language-server/tests/hover/hover.test.js
+++ b/packages/tailwindcss-language-server/tests/hover/hover.test.js
@@ -1,5 +1,7 @@
-import { test } from 'vitest'
+import { expect, test } from 'vitest'
import { withFixture } from '../common'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
withFixture('basic', (c) => {
async function testHover(
@@ -396,7 +398,14 @@ withFixture('v4/basic', (c) => {
expected: {
contents: {
kind: 'markdown',
- value: ['```plaintext', '80rem /* 1280px */', '```'].join('\n'),
+ value: [
+ //
+ '```css',
+ '@theme {',
+ ' --breakpoint-xl: 80rem /* 1280px */;',
+ '}',
+ '```',
+ ].join('\n'),
},
range: {
start: { line: 0, character: 23 },
@@ -544,3 +553,72 @@ withFixture('v4/path-mappings', (c) => {
},
})
})
+
+defineTest({
+ name: 'Can hover showing theme values used in var(…) and theme(…) functions',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'css',
+ text: css`
+ .foo {
+ color: theme(--color-black);
+ }
+ .bar {
+ color: var(--color-black);
+ }
+ `,
+ })
+
+ // color: theme(--color-black);
+ // ^
+ let hoverTheme = await doc.hover({ line: 1, character: 18 })
+
+ // color: var(--color-black);
+ // ^
+ let hoverVar = await doc.hover({ line: 4, character: 16 })
+
+ expect(hoverTheme).toEqual({
+ contents: {
+ kind: 'markdown',
+ value: [
+ //
+ '```css',
+ '@theme {',
+ ' --color-black: #000;',
+ '}',
+ '```',
+ ].join('\n'),
+ },
+ range: {
+ start: { line: 1, character: 15 },
+ end: { line: 1, character: 28 },
+ },
+ })
+
+ expect(hoverVar).toEqual({
+ contents: {
+ kind: 'markdown',
+ value: [
+ //
+ '```css',
+ '@theme {',
+ ' --color-black: #000;',
+ '}',
+ '```',
+ ].join('\n'),
+ },
+ range: {
+ start: { line: 4, character: 13 },
+ end: { line: 4, character: 26 },
+ },
+ })
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidConfigPathDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidConfigPathDiagnostics.ts
index d20655e3..76864281 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidConfigPathDiagnostics.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidConfigPathDiagnostics.ts
@@ -186,6 +186,11 @@ export function getInvalidConfigPathDiagnostics(
findHelperFunctionsInDocument(state, document).forEach((helperFn) => {
let base = helperFn.helper === 'theme' ? ['theme'] : []
+
+ // var(…) may not refer to theme values but other values in the cascade
+ // so they can't be unconditionally validated
+ if (helperFn.helper === 'var') return
+
let result = validateConfigPath(state, helperFn.path, base)
if (result.isValid === true) {
diff --git a/packages/tailwindcss-language-service/src/hoverProvider.ts b/packages/tailwindcss-language-service/src/hoverProvider.ts
index 1db68bed..583cc80f 100644
--- a/packages/tailwindcss-language-service/src/hoverProvider.ts
+++ b/packages/tailwindcss-language-service/src/hoverProvider.ts
@@ -53,6 +53,8 @@ async function provideCssHelperHover(
for (let helperFn of helperFns) {
if (!isWithinRange(position, helperFn.ranges.path)) continue
+ if (helperFn.helper === 'var' && !state.v4) continue
+
let validated = validateConfigPath(
state,
helperFn.path,
@@ -67,8 +69,21 @@ async function provideCssHelperHover(
value = addPixelEquivalentsToValue(value, settings.tailwindCSS.rootFontSize)
}
+ let lines = ['```plaintext', value, '```']
+
+ if (state.v4 && helperFn.path.startsWith('--')) {
+ lines = [
+ //
+ '```css',
+ '@theme {',
+ ` ${helperFn.path}: ${value};`,
+ '}',
+ '```',
+ ]
+ }
+
return {
- contents: { kind: 'markdown', value: ['```plaintext', value, '```'].join('\n') },
+ contents: { kind: 'markdown', value: lines.join('\n') },
range: helperFn.ranges.path,
}
}
diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts
index 03218798..9118403d 100644
--- a/packages/tailwindcss-language-service/src/util/find.ts
+++ b/packages/tailwindcss-language-service/src/util/find.ts
@@ -405,7 +405,7 @@ export function findHelperFunctionsInRange(
): DocumentHelperFunction[] {
const text = getTextWithoutComments(doc, 'css', range)
let matches = findAll(
- /(?[\W])(?config|theme|--theme)(?\(\s*)(?[^)]*?)\s*\)/g,
+ /(?[\W])(?config|theme|--theme|var)(?\(\s*)(?[^)]*?)\s*\)/g,
text,
)
@@ -450,10 +450,12 @@ export function findHelperFunctionsInRange(
match.groups.helper.length +
match.groups.innerPrefix.length
- let helper: 'config' | 'theme' = 'config'
+ let helper: 'config' | 'theme' | 'var' = 'config'
if (match.groups.helper === 'theme' || match.groups.helper === '--theme') {
helper = 'theme'
+ } else if (match.groups.helper === 'var') {
+ helper = 'var'
}
return {
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 119e5f59..8c819eb8 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -161,7 +161,7 @@ export type DocumentClassName = {
}
export type DocumentHelperFunction = {
- helper: 'theme' | 'config'
+ helper: 'theme' | 'config' | 'var'
path: string
ranges: {
full: Range
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 421543f5..d55074be 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -5,6 +5,8 @@
- Hide completions from CSS language server inside `@import "…" source(…)` ([#1091](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1091))
- Bump bundled v4 fallback to v4.1.1 ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
- Show color swatches for most new v4.1 utilities ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
+- Support theme key hovers in the CSS `var()` function ([#1289](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1289))
+- Show theme key hovers inside `@theme` for better context and syntax highlighting ([#1289](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1289))
# 0.14.12
From 5ffa254c4d239747755283795f91209296303c15 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 2 Apr 2025 10:26:31 -0400
Subject: [PATCH 044/108] 0.14.13
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index d96255dd..7d142c90 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.12",
+ "version": "0.14.13",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 48fae16e..23907838 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.12",
+ "version": "0.14.13",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index d55074be..6a8c2dac 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.13
+
- Hide completions from CSS language server inside `@import "…" source(…)` ([#1091](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1091))
- Bump bundled v4 fallback to v4.1.1 ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
- Show color swatches for most new v4.1 utilities ([#1294](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1294))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 26af65d3..4f55fb4a 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.12",
+ "version": "0.14.13",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 6c573a801d6afc65649951adb91cd5bb7b927a52 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Fri, 4 Apr 2025 13:16:28 -0400
Subject: [PATCH 045/108] v4: Improve DX around completions when prefixes are
in use (#1292)
Fixes #1291
- [x] Really needs tests
---
.../tests/completions/completions.test.js | 74 +++++++++++++++++++
.../src/completionProvider.ts | 36 ++++-----
.../src/util/getVariantsFromClassName.ts | 6 ++
3 files changed, 98 insertions(+), 18 deletions(-)
diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js
index feb4999f..ce5c7831 100644
--- a/packages/tailwindcss-language-server/tests/completions/completions.test.js
+++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js
@@ -740,6 +740,80 @@ defineTest({
},
})
+defineTest({
+ name: 'v4: Completions show after a variant arbitrary value, using prefixes',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss' prefix(tw);
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 26 })
+
+ expect(completion?.items.length).toBe(19236)
+ },
+})
+
+defineTest({
+ name: 'v4: Variant and utility suggestions show prefix when one has been typed',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss' prefix(tw);
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 12 })
+
+ expect(completion?.items.length).toBe(19237)
+
+ // Verify that variants and utilities are all prefixed
+ let prefixed = completion.items.filter((item) => !item.label.startsWith('tw:'))
+ expect(prefixed).toHaveLength(0)
+ },
+})
+
+defineTest({
+ name: 'v4: Variant and utility suggestions hide prefix when it has been typed',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss' prefix(tw);
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let document = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let completion = await document.completions({ line: 0, character: 15 })
+
+ expect(completion?.items.length).toBe(19236)
+
+ // Verify that no variants and utilities have prefixes
+ let prefixed = completion.items.filter((item) => item.label.startsWith('tw:'))
+ expect(prefixed).toHaveLength(0)
+ },
+})
+
defineTest({
name: 'v4: Completions show inside class functions in JS/TS files',
fs: {
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 5c0c5552..843e9a8e 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -261,29 +261,25 @@ export function completionsFromClassList(
// TODO: This is a bit of a hack
if (prefix.length > 0) {
- // No variants seen: suggest the prefix only
+ // No variants seen:
+ // - suggest the prefix as a variant
+ // - Modify the remaining items to include the prefix in the variant name
if (existingVariants.length === 0) {
- items = items.slice(0, 1)
+ items = items.map((item, idx) => {
+ if (idx === 0) return item
- return withDefaults(
- {
- isIncomplete: false,
- items,
- },
- {
- data: {
- ...(state.completionItemData ?? {}),
- ...(important ? { important } : {}),
- variants: existingVariants,
- },
- range: replacementRange,
- },
- state.editor.capabilities.itemDefaults,
- )
+ item.label = `${prefix}:${item.label}`
+
+ if (item.textEditText) {
+ item.textEditText = `${prefix}:${item.textEditText}`
+ }
+
+ return item
+ })
}
// The first variant is not the prefix: don't suggest anything
- if (existingVariants[0] !== prefix) {
+ if (existingVariants.length > 0 && existingVariants[0] !== prefix) {
return null
}
}
@@ -304,6 +300,10 @@ export function completionsFromClassList(
documentation = formatColor(color)
}
+ if (prefix.length > 0 && existingVariants.length === 0) {
+ className = `${prefix}:${className}`
+ }
+
items.push({
label: className,
kind,
diff --git a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
index c30e729a..823b20f8 100644
--- a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
+++ b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
@@ -33,6 +33,12 @@ export function getVariantsFromClassName(
// NOTE: This should never happen
if (!state.designSystem) return false
+ let prefix = state.designSystem.theme.prefix ?? ''
+
+ if (prefix !== '') {
+ className = `${prefix}:${className}`
+ }
+
// We don't use `compile()` so there's no overhead from PostCSS
let compiled = state.designSystem.candidatesToCss([className])
From f8313abe86a548745ddbdcf6942e359a69706b9e Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 12:57:56 -0400
Subject: [PATCH 046/108] Fix problem with too many ripgrep processes being
spawned by VSCode (#1287)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We run a file search on a per-workspace basis but we wait until a file
has been opened. However, there are two big problems here:
- A file being "opened" does **not** mean it is visible. Just that an
extension has, effectively, taken an interest in it, can read its
contents, etc. This happens for things like tsconfig files, some files
inside a `.git` folder, etc…
- We're running the search any time we see an opened document. What
should happen is that we run the search when a document is opened _and
visible_, the language server has not started, and we need to checking a
workspace folder that has not been searched yet.
This code here needs to be restructured to ensure that these searches
only run when they are needed. If the searches don't return anything or
time out then they should not be run again. Notifications from file
watching should take care of the rest in case the initial search turned
up nothing and the user adds a file that should cause the server to
start.
Fixes #986 (for real maybe this time??)
---
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
packages/vscode-tailwindcss/src/analyze.ts | 93 +++++++++
packages/vscode-tailwindcss/src/api.ts | 49 +++++
packages/vscode-tailwindcss/src/exclusions.ts | 49 +++++
packages/vscode-tailwindcss/src/extension.ts | 190 +++---------------
5 files changed, 222 insertions(+), 161 deletions(-)
create mode 100644 packages/vscode-tailwindcss/src/analyze.ts
create mode 100644 packages/vscode-tailwindcss/src/api.ts
create mode 100644 packages/vscode-tailwindcss/src/exclusions.ts
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 6a8c2dac..661c8d17 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Only scan the file system once when needed ([#1287](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1287))
# 0.14.13
diff --git a/packages/vscode-tailwindcss/src/analyze.ts b/packages/vscode-tailwindcss/src/analyze.ts
new file mode 100644
index 00000000..ec9b2b9a
--- /dev/null
+++ b/packages/vscode-tailwindcss/src/analyze.ts
@@ -0,0 +1,93 @@
+import { workspace, RelativePattern, CancellationToken, Uri, WorkspaceFolder } from 'vscode'
+import braces from 'braces'
+import { CONFIG_GLOB, CSS_GLOB } from '@tailwindcss/language-server/src/lib/constants'
+import { getExcludePatterns } from './exclusions'
+
+export interface SearchOptions {
+ folders: readonly WorkspaceFolder[]
+ token: CancellationToken
+}
+
+export async function anyWorkspaceFoldersNeedServer({ folders, token }: SearchOptions) {
+ // An explicit config file setting means we need the server
+ for (let folder of folders) {
+ let settings = workspace.getConfiguration('tailwindCSS', folder)
+ let configFilePath = settings.get('experimental.configFile')
+
+ // No setting provided
+ if (!configFilePath) continue
+
+ // Ths config file may be a string:
+ // A path pointing to a CSS or JS config file
+ if (typeof configFilePath === 'string') return true
+
+ // Ths config file may be an object:
+ // A map of config files to one or more globs
+ //
+ // If we get an empty object the language server will do a search anyway so
+ // we'll act as if no option was passed to be consistent
+ if (typeof configFilePath === 'object' && Object.values(configFilePath).length > 0) return true
+ }
+
+ let configs: Array<() => Thenable> = []
+ let stylesheets: Array<() => Thenable> = []
+
+ for (let folder of folders) {
+ let exclusions = getExcludePatterns(folder).flatMap((pattern) => braces.expand(pattern))
+ let exclude = `{${exclusions.join(',').replace(/{/g, '%7B').replace(/}/g, '%7D')}}`
+
+ configs.push(() =>
+ workspace.findFiles(
+ new RelativePattern(folder, `**/${CONFIG_GLOB}`),
+ exclude,
+ undefined,
+ token,
+ ),
+ )
+
+ stylesheets.push(() =>
+ workspace.findFiles(new RelativePattern(folder, `**/${CSS_GLOB}`), exclude, undefined, token),
+ )
+ }
+
+ // If we find a config file then we need the server
+ let configUrls = await Promise.all(configs.map((fn) => fn()))
+ for (let group of configUrls) {
+ if (group.length > 0) {
+ return true
+ }
+ }
+
+ // If we find a possibly-related stylesheet then we need the server
+ // The step is done last because it requires reading individual files
+ // to determine if the server should be started.
+ //
+ // This is also, unfortunately, prone to starting the server unncessarily
+ // in projects that don't use TailwindCSS so we do this one-by-one instead
+ // of all at once to keep disk I/O low.
+ let stylesheetUrls = await Promise.all(stylesheets.map((fn) => fn()))
+ for (let group of stylesheetUrls) {
+ for (let file of group) {
+ if (await fileMayBeTailwindRelated(file)) {
+ return true
+ }
+ }
+ }
+}
+
+let HAS_CONFIG = /@config\s*['"]/
+let HAS_IMPORT = /@import\s*['"]/
+let HAS_TAILWIND = /@tailwind\s*[^;]+;/
+let HAS_THEME = /@theme\s*\{/
+
+export async function fileMayBeTailwindRelated(uri: Uri) {
+ let buffer = await workspace.fs.readFile(uri)
+ let contents = buffer.toString()
+
+ return (
+ HAS_CONFIG.test(contents) ||
+ HAS_IMPORT.test(contents) ||
+ HAS_TAILWIND.test(contents) ||
+ HAS_THEME.test(contents)
+ )
+}
diff --git a/packages/vscode-tailwindcss/src/api.ts b/packages/vscode-tailwindcss/src/api.ts
new file mode 100644
index 00000000..d7f67de6
--- /dev/null
+++ b/packages/vscode-tailwindcss/src/api.ts
@@ -0,0 +1,49 @@
+import { workspace, CancellationTokenSource, OutputChannel, ExtensionContext, Uri } from 'vscode'
+import { anyWorkspaceFoldersNeedServer, fileMayBeTailwindRelated } from './analyze'
+
+interface ApiOptions {
+ context: ExtensionContext
+ outputChannel: OutputChannel
+}
+
+export async function createApi({ context, outputChannel }: ApiOptions) {
+ let folderAnalysis: Promise | null = null
+
+ async function workspaceNeedsLanguageServer() {
+ if (folderAnalysis) return folderAnalysis
+
+ let source: CancellationTokenSource | null = new CancellationTokenSource()
+ source.token.onCancellationRequested(() => {
+ source?.dispose()
+ source = null
+
+ outputChannel.appendLine(
+ 'Server was not started. Search for Tailwind CSS-related files was taking too long.',
+ )
+ })
+
+ // Cancel the search after roughly 15 seconds
+ setTimeout(() => source?.cancel(), 15_000)
+ context.subscriptions.push(source)
+
+ folderAnalysis ??= anyWorkspaceFoldersNeedServer({
+ token: source.token,
+ folders: workspace.workspaceFolders ?? [],
+ })
+
+ let result = await folderAnalysis
+ source?.dispose()
+ return result
+ }
+
+ async function stylesheetNeedsLanguageServer(uri: Uri) {
+ outputChannel.appendLine(`Checking if ${uri.fsPath} may be Tailwind-related…`)
+
+ return fileMayBeTailwindRelated(uri)
+ }
+
+ return {
+ workspaceNeedsLanguageServer,
+ stylesheetNeedsLanguageServer,
+ }
+}
diff --git a/packages/vscode-tailwindcss/src/exclusions.ts b/packages/vscode-tailwindcss/src/exclusions.ts
new file mode 100644
index 00000000..46ffd599
--- /dev/null
+++ b/packages/vscode-tailwindcss/src/exclusions.ts
@@ -0,0 +1,49 @@
+import {
+ workspace,
+ type WorkspaceConfiguration,
+ type ConfigurationScope,
+ type WorkspaceFolder,
+} from 'vscode'
+import picomatch from 'picomatch'
+import * as path from 'node:path'
+
+function getGlobalExcludePatterns(scope: ConfigurationScope | null): string[] {
+ return Object.entries(workspace.getConfiguration('files', scope)?.get('exclude') ?? [])
+ .filter(([, value]) => value === true)
+ .map(([key]) => key)
+ .filter(Boolean)
+}
+
+export function getExcludePatterns(scope: ConfigurationScope | null): string[] {
+ return [
+ ...getGlobalExcludePatterns(scope),
+ ...(workspace.getConfiguration('tailwindCSS', scope).get('files.exclude')).filter(
+ Boolean,
+ ),
+ ]
+}
+
+export function isExcluded(file: string, folder: WorkspaceFolder): boolean {
+ for (let pattern of getExcludePatterns(folder)) {
+ let matcher = picomatch(path.join(folder.uri.fsPath, pattern))
+
+ if (matcher(file)) {
+ return true
+ }
+ }
+
+ return false
+}
+
+export function mergeExcludes(
+ settings: WorkspaceConfiguration,
+ scope: ConfigurationScope | null,
+): any {
+ return {
+ ...settings,
+ files: {
+ ...settings.files,
+ exclude: getExcludePatterns(scope),
+ },
+ }
+}
diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts
index a3748616..467d4e08 100755
--- a/packages/vscode-tailwindcss/src/extension.ts
+++ b/packages/vscode-tailwindcss/src/extension.ts
@@ -4,9 +4,7 @@ import type {
TextDocument,
WorkspaceFolder,
ConfigurationScope,
- WorkspaceConfiguration,
Selection,
- CancellationToken,
} from 'vscode'
import {
workspace as Workspace,
@@ -16,8 +14,6 @@ import {
SymbolInformation,
Position,
Range,
- RelativePattern,
- CancellationTokenSource,
} from 'vscode'
import type {
DocumentFilter,
@@ -34,11 +30,11 @@ import { languages as defaultLanguages } from '@tailwindcss/language-service/src
import * as semver from '@tailwindcss/language-service/src/util/semver'
import isObject from '@tailwindcss/language-service/src/util/isObject'
import namedColors from 'color-name'
-import picomatch from 'picomatch'
import { CONFIG_GLOB, CSS_GLOB } from '@tailwindcss/language-server/src/lib/constants'
-import braces from 'braces'
import normalizePath from 'normalize-path'
import * as servers from './servers/index'
+import { isExcluded, mergeExcludes } from './exclusions'
+import { createApi } from './api'
const colorNames = Object.keys(namedColors)
@@ -52,60 +48,6 @@ function getUserLanguages(folder?: WorkspaceFolder): Record {
return isObject(langs) ? langs : {}
}
-function getGlobalExcludePatterns(scope: ConfigurationScope | null): string[] {
- return Object.entries(Workspace.getConfiguration('files', scope)?.get('exclude') ?? [])
- .filter(([, value]) => value === true)
- .map(([key]) => key)
- .filter(Boolean)
-}
-
-function getExcludePatterns(scope: ConfigurationScope | null): string[] {
- return [
- ...getGlobalExcludePatterns(scope),
- ...(Workspace.getConfiguration('tailwindCSS', scope).get('files.exclude')).filter(
- Boolean,
- ),
- ]
-}
-
-function isExcluded(file: string, folder: WorkspaceFolder): boolean {
- for (let pattern of getExcludePatterns(folder)) {
- let matcher = picomatch(path.join(folder.uri.fsPath, pattern))
-
- if (matcher(file)) {
- return true
- }
- }
-
- return false
-}
-
-function mergeExcludes(settings: WorkspaceConfiguration, scope: ConfigurationScope | null): any {
- return {
- ...settings,
- files: {
- ...settings.files,
- exclude: getExcludePatterns(scope),
- },
- }
-}
-
-async function fileMayBeTailwindRelated(uri: Uri) {
- let contents = (await Workspace.fs.readFile(uri)).toString()
-
- let HAS_CONFIG = /@config\s*['"]/
- let HAS_IMPORT = /@import\s*['"]/
- let HAS_TAILWIND = /@tailwind\s*[^;]+;/
- let HAS_THEME = /@theme\s*\{/
-
- return (
- HAS_CONFIG.test(contents) ||
- HAS_IMPORT.test(contents) ||
- HAS_TAILWIND.test(contents) ||
- HAS_THEME.test(contents)
- )
-}
-
function selectionsAreEqual(
aSelections: readonly Selection[],
bSelections: readonly Selection[],
@@ -177,6 +119,12 @@ function resetActiveTextEditorContext(): void {
export async function activate(context: ExtensionContext) {
let outputChannel = Window.createOutputChannel(CLIENT_NAME)
+
+ let api = await createApi({
+ context,
+ outputChannel,
+ })
+
context.subscriptions.push(outputChannel)
context.subscriptions.push(
commands.registerCommand('tailwindCSS.showOutput', () => {
@@ -266,10 +214,10 @@ export async function activate(context: ExtensionContext) {
let configWatcher = Workspace.createFileSystemWatcher(`**/${CONFIG_GLOB}`, false, true, true)
configWatcher.onDidCreate(async (uri) => {
+ if (currentClient) return
let folder = Workspace.getWorkspaceFolder(uri)
- if (!folder || isExcluded(uri.fsPath, folder)) {
- return
- }
+ if (!folder || isExcluded(uri.fsPath, folder)) return
+
await bootWorkspaceClient()
})
@@ -278,13 +226,12 @@ export async function activate(context: ExtensionContext) {
let cssWatcher = Workspace.createFileSystemWatcher(`**/${CSS_GLOB}`, false, false, true)
async function bootClientIfCssFileMayBeTailwindRelated(uri: Uri) {
+ if (currentClient) return
let folder = Workspace.getWorkspaceFolder(uri)
- if (!folder || isExcluded(uri.fsPath, folder)) {
- return
- }
- if (await fileMayBeTailwindRelated(uri)) {
- await bootWorkspaceClient()
- }
+ if (!folder || isExcluded(uri.fsPath, folder)) return
+ if (!(await api.stylesheetNeedsLanguageServer(uri))) return
+
+ await bootWorkspaceClient()
}
cssWatcher.onDidCreate(bootClientIfCssFileMayBeTailwindRelated)
@@ -578,111 +525,34 @@ export async function activate(context: ExtensionContext) {
return client
}
- async function bootClientIfNeeded(): Promise {
- if (currentClient) {
- return
- }
-
- let source: CancellationTokenSource | null = new CancellationTokenSource()
- source.token.onCancellationRequested(() => {
- source?.dispose()
- source = null
- outputChannel.appendLine(
- 'Server was not started. Search for Tailwind CSS-related files was taking too long.',
- )
- })
-
- // Cancel the search after roughly 15 seconds
- setTimeout(() => source?.cancel(), 15_000)
-
- if (!(await anyFolderNeedsLanguageServer(Workspace.workspaceFolders ?? [], source!.token))) {
- source?.dispose()
- return
- }
-
- source?.dispose()
-
- await bootWorkspaceClient()
- }
-
- async function anyFolderNeedsLanguageServer(
- folders: readonly WorkspaceFolder[],
- token: CancellationToken,
- ): Promise {
- for (let folder of folders) {
- if (await folderNeedsLanguageServer(folder, token)) {
- return true
- }
- }
-
- return false
- }
-
- async function folderNeedsLanguageServer(
- folder: WorkspaceFolder,
- token: CancellationToken,
- ): Promise {
- let settings = Workspace.getConfiguration('tailwindCSS', folder)
- if (settings.get('experimental.configFile') !== null) {
- return true
- }
-
- let exclude = `{${getExcludePatterns(folder)
- .flatMap((pattern) => braces.expand(pattern))
- .join(',')
- .replace(/{/g, '%7B')
- .replace(/}/g, '%7D')}}`
-
- let configFiles = await Workspace.findFiles(
- new RelativePattern(folder, `**/${CONFIG_GLOB}`),
- exclude,
- 1,
- token,
- )
-
- for (let file of configFiles) {
- return true
- }
-
- let cssFiles = await Workspace.findFiles(
- new RelativePattern(folder, `**/${CSS_GLOB}`),
- exclude,
- undefined,
- token,
- )
-
- for (let file of cssFiles) {
- outputChannel.appendLine(`Checking if ${file.fsPath} may be Tailwind-related…`)
-
- if (await fileMayBeTailwindRelated(file)) {
- return true
- }
- }
-
- return false
- }
-
+ /**
+ * Note that this method can fire *many* times even for documents that are
+ * not in a visible editor. It's critical that this doesn't start any
+ * expensive operations more than is necessary.
+ */
async function didOpenTextDocument(document: TextDocument): Promise {
if (document.languageId === 'tailwindcss') {
servers.css.boot(context, outputChannel)
}
+ if (currentClient) return
+
// We are only interested in language mode text
- if (document.uri.scheme !== 'file') {
- return
- }
+ if (document.uri.scheme !== 'file') return
- let uri = document.uri
- let folder = Workspace.getWorkspaceFolder(uri)
+ let folder = Workspace.getWorkspaceFolder(document.uri)
// Files outside a folder can't be handled. This might depend on the language.
// Single file languages like JSON might handle files outside the workspace folders.
- if (!folder) return
+ if (!folder || isExcluded(document.uri.fsPath, folder)) return
+
+ if (!(await api.workspaceNeedsLanguageServer())) return
- await bootClientIfNeeded()
+ await bootWorkspaceClient()
}
context.subscriptions.push(Workspace.onDidOpenTextDocument(didOpenTextDocument))
+
Workspace.textDocuments.forEach(didOpenTextDocument)
context.subscriptions.push(
Workspace.onDidChangeWorkspaceFolders(async () => {
From 2332990e96e7df3836fd7dee2d1c94d6b162df96 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 13:13:28 -0400
Subject: [PATCH 047/108] Don't follow recursive symlinks during project
discovery (#1270)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Project discovery is done by crawling the workspace with `fast-glob`
looking for CSS files, JS/TS config files, Sass/Less files, etc…
A major problem with this approach though is that `fast-glob` crawls the
filesystem and traverses into symlinked directories. If the symlink
points back to a parent folder the crawler may cause a handful of
issues:
- An infinite traversal loop
- Out of memory crashes (causing the server to restart repeatedly)
- A rather long discovery time (10s+)
- Errors because of file path length limits (esp on Windows)
I've rewritten the traversal to use `tinyglobby` which is a
`fast-glob`-compatible replacement. It avoids all of these issues due to
its use of `fdir` which explicitly detects and avoids traversal of
recursive symlinks.
All existing tests pass as well as an additional test that previously
timed out due to the use of recursive symlinks. This PR is going to
require extensive testing on Windows with network shares, mapped drives,
etc…
Fixes #1056
Fixes #1185
---
.../tailwindcss-language-server/package.json | 2 +-
.../src/project-locator.test.ts | 34 +++++++++++++
.../src/project-locator.ts | 9 ++--
.../tests/prepare.mjs | 12 +++--
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
pnpm-lock.yaml | 49 +++++++++----------
6 files changed, 72 insertions(+), 35 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 7d142c90..c4dc2984 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -67,7 +67,6 @@
"dset": "3.1.4",
"enhanced-resolve": "^5.16.1",
"esbuild": "^0.25.0",
- "fast-glob": "3.2.4",
"find-up": "5.0.0",
"jiti": "^2.3.3",
"klona": "2.0.4",
@@ -85,6 +84,7 @@
"stack-trace": "0.0.10",
"tailwindcss": "3.4.17",
"tailwindcss-v4": "npm:tailwindcss@4.1.1",
+ "tinyglobby": "^0.2.12",
"tsconfck": "^3.1.4",
"tsconfig-paths": "^4.2.0",
"typescript": "5.3.3",
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index 3699c840..f43479ae 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -428,6 +428,40 @@ testLocator({
],
})
+testLocator({
+ name: 'Recursive symlinks do not cause infinite traversal loops',
+ fs: {
+ 'src/a/b/c/index.css': css`
+ @import 'tailwindcss';
+ `,
+ 'src/a/b/c/z': symlinkTo('src', 'dir'),
+ 'src/a/b/x': symlinkTo('src', 'dir'),
+ 'src/a/b/y': symlinkTo('src', 'dir'),
+ 'src/a/b/z': symlinkTo('src', 'dir'),
+ 'src/a/x': symlinkTo('src', 'dir'),
+
+ 'src/b/c/d/z': symlinkTo('src', 'dir'),
+ 'src/b/c/d/index.css': css``,
+ 'src/b/c/x': symlinkTo('src', 'dir'),
+ 'src/b/c/y': symlinkTo('src', 'dir'),
+ 'src/b/c/z': symlinkTo('src', 'dir'),
+ 'src/b/x': symlinkTo('src', 'dir'),
+
+ 'src/c/d/e/z': symlinkTo('src', 'dir'),
+ 'src/c/d/x': symlinkTo('src', 'dir'),
+ 'src/c/d/y': symlinkTo('src', 'dir'),
+ 'src/c/d/z': symlinkTo('src', 'dir'),
+ 'src/c/x': symlinkTo('src', 'dir'),
+ },
+ expected: [
+ {
+ version: '4.1.1 (bundled)',
+ config: '/src/a/b/c/index.css',
+ content: [],
+ },
+ ],
+})
+
// ---
function testLocator({
diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts
index c04738bf..de562b63 100644
--- a/packages/tailwindcss-language-server/src/project-locator.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.ts
@@ -1,7 +1,6 @@
-import * as os from 'node:os'
import * as path from 'node:path'
import * as fs from 'node:fs/promises'
-import glob from 'fast-glob'
+import { glob } from 'tinyglobby'
import picomatch from 'picomatch'
import type { Settings } from '@tailwindcss/language-service/src/util/state'
import { CONFIG_GLOB, CSS_GLOB } from './lib/constants'
@@ -276,14 +275,14 @@ export class ProjectLocator {
private async findConfigs(): Promise {
// Look for config files and CSS files
- let files = await glob([`**/${CONFIG_GLOB}`, `**/${CSS_GLOB}`], {
+ let files = await glob({
+ patterns: [`**/${CONFIG_GLOB}`, `**/${CSS_GLOB}`],
cwd: this.base,
ignore: this.settings.tailwindCSS.files.exclude,
onlyFiles: true,
absolute: true,
- suppressErrors: true,
+ followSymbolicLinks: true,
dot: true,
- concurrency: Math.max(os.cpus().length, 1),
})
let realpaths = await Promise.all(files.map((file) => fs.realpath(file)))
diff --git a/packages/tailwindcss-language-server/tests/prepare.mjs b/packages/tailwindcss-language-server/tests/prepare.mjs
index 19b2c6d5..c529e630 100644
--- a/packages/tailwindcss-language-server/tests/prepare.mjs
+++ b/packages/tailwindcss-language-server/tests/prepare.mjs
@@ -2,19 +2,23 @@ import { exec } from 'node:child_process'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
import { promisify } from 'node:util'
-import glob from 'fast-glob'
+import { glob } from 'tinyglobby'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
-const fixtures = glob.sync(['tests/fixtures/*/package.json', 'tests/fixtures/v4/*/package.json'], {
- cwd: path.resolve(__dirname, '..'),
+const root = path.resolve(__dirname, '..')
+
+const fixtures = await glob({
+ cwd: root,
+ patterns: ['tests/fixtures/*/package.json', 'tests/fixtures/v4/*/package.json'],
+ absolute: true,
})
const execAsync = promisify(exec)
await Promise.all(
fixtures.map(async (fixture) => {
- console.log(`Installing dependencies for ${fixture}`)
+ console.log(`Installing dependencies for ${path.relative(root, fixture)}`)
await execAsync('npm install', { cwd: path.dirname(fixture) })
}),
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 661c8d17..1750eb69 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- Only scan the file system once when needed ([#1287](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1287))
+- Don't follow recursive symlinks when searching for projects ([#1270](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1270))
# 0.14.13
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e879d3a6..c4e0edaf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -131,9 +131,6 @@ importers:
esbuild:
specifier: ^0.25.0
version: 0.25.0
- fast-glob:
- specifier: 3.2.4
- version: 3.2.4
find-up:
specifier: 5.0.0
version: 5.0.0
@@ -185,6 +182,9 @@ importers:
tailwindcss-v4:
specifier: npm:tailwindcss@4.1.1
version: tailwindcss@4.1.1
+ tinyglobby:
+ specifier: ^0.2.12
+ version: 0.2.12
tsconfck:
specifier: ^3.1.4
version: 3.1.4(typescript@5.3.3)
@@ -1480,10 +1480,6 @@ packages:
resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==}
engines: {node: '>=12.0.0'}
- fast-glob@3.2.4:
- resolution: {integrity: sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==}
- engines: {node: '>=8'}
-
fast-glob@3.3.2:
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
engines: {node: '>=8.6.0'}
@@ -1494,6 +1490,14 @@ packages:
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+ fdir@6.4.3:
+ resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -1815,10 +1819,6 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
- micromatch@4.0.7:
- resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
- engines: {node: '>=8.6'}
-
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -2467,6 +2467,10 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+ tinyglobby@0.2.12:
+ resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
+ engines: {node: '>=12.0.0'}
+
tinypool@1.0.2:
resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -3717,15 +3721,6 @@ snapshots:
expect-type@1.2.0: {}
- fast-glob@3.2.4:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.7
- picomatch: 2.3.1
-
fast-glob@3.3.2:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -3742,6 +3737,10 @@ snapshots:
dependencies:
pend: 1.2.0
+ fdir@6.4.3(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -4045,11 +4044,6 @@ snapshots:
merge2@1.4.1: {}
- micromatch@4.0.7:
- dependencies:
- braces: 3.0.3
- picomatch: 2.3.1
-
micromatch@4.0.8:
dependencies:
braces: 3.0.3
@@ -4743,6 +4737,11 @@ snapshots:
tinyexec@0.3.2: {}
+ tinyglobby@0.2.12:
+ dependencies:
+ fdir: 6.4.3(picomatch@4.0.2)
+ picomatch: 4.0.2
+
tinypool@1.0.2: {}
tinyrainbow@2.0.0: {}
From 0610bf4df2170b23a4a4c14dfabf1ccff8804cc7 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 14:03:51 -0400
Subject: [PATCH 048/108] Handle when project config is re-created (#1300)
If a project's main CSS config file is deleted and then re-created the
server won't re-create the project.
For now we'll restart the server when events like this happen which will
ensure that the filesystem is re-scanned and the files can be picked up
again.
---
.../src/projects.ts | 5 +
.../src/testing/index.ts | 42 +++--
.../tailwindcss-language-server/src/tw.ts | 44 ++++-
.../tests/env/restart.test.ts | 169 ++++++++++++++++++
.../tests/utils/client.ts | 8 +-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
6 files changed, 253 insertions(+), 16 deletions(-)
create mode 100644 packages/tailwindcss-language-server/tests/env/restart.test.ts
diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index a8012920..25aacd5f 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -1164,6 +1164,11 @@ export async function createProjectService(
let elapsed = process.hrtime.bigint() - start
console.log(`---- RELOADED IN ${(Number(elapsed) / 1e6).toFixed(2)}ms ----`)
+
+ let isTestMode = params.initializationOptions?.testMode ?? false
+ if (!isTestMode) return
+
+ connection.sendNotification('@/tailwindCSS/projectReloaded')
},
state,
diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts
index 91a07b81..0a11bb9d 100644
--- a/packages/tailwindcss-language-server/src/testing/index.ts
+++ b/packages/tailwindcss-language-server/src/testing/index.ts
@@ -1,13 +1,20 @@
-import { onTestFinished, test, TestOptions } from 'vitest'
+import { onTestFinished, test, TestContext, TestOptions } from 'vitest'
import * as os from 'node:os'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as proc from 'node:child_process'
import dedent from 'dedent'
-export interface TestUtils {
+export interface TestUtils> {
/** The "cwd" for this test */
root: string
+
+ /**
+ * The input for this test — taken from the `inputs` in the test config
+ *
+ * @see {TestConfig}
+ */
+ input?: TestInput
}
export interface StorageSymlink {
@@ -21,29 +28,39 @@ export interface Storage {
[filePath: string]: string | Uint8Array | StorageSymlink
}
-export interface TestConfig {
+export interface TestConfig> {
name: string
+ inputs?: TestInput[]
+
fs?: Storage
debug?: boolean
- prepare?(utils: TestUtils): Promise
- handle(utils: TestUtils & Extras): void | Promise
+ prepare?(utils: TestUtils): Promise
+ handle(utils: TestUtils & Extras): void | Promise
options?: TestOptions
}
-export function defineTest(config: TestConfig) {
- return test(config.name, config.options ?? {}, async ({ expect }) => {
- let utils = await setup(config)
+export function defineTest(config: TestConfig) {
+ async function runTest(ctx: TestContext, input?: I) {
+ let utils = await setup(config, input)
let extras = await config.prepare?.(utils)
await config.handle({
...utils,
...extras,
})
- })
+ }
+
+ if (config.inputs) {
+ return test.for(config.inputs ?? [])(config.name, config.options ?? {}, (input, ctx) =>
+ runTest(ctx, input),
+ )
+ }
+
+ return test(config.name, config.options ?? {}, runTest)
}
-async function setup(config: TestConfig): Promise {
+async function setup(config: TestConfig, input: I): Promise> {
let randomId = Math.random().toString(36).substring(7)
let baseDir = path.resolve(process.cwd(), `../../.debug/${randomId}`)
@@ -56,7 +73,7 @@ async function setup(config: TestConfig): Promise {
await installDependencies(baseDir, config.fs)
}
- onTestFinished(async (result) => {
+ onTestFinished(async (ctx) => {
// Once done, move all the files to a new location
try {
await fs.rename(baseDir, doneDir)
@@ -66,7 +83,7 @@ async function setup(config: TestConfig): Promise {
console.error('Failed to move test files to done directory')
}
- if (result.state === 'fail') return
+ if (ctx.task.result?.state === 'fail') return
if (path.sep === '\\') return
@@ -79,6 +96,7 @@ async function setup(config: TestConfig): Promise {
return {
root: baseDir,
+ input,
}
}
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index d5cfba20..a0b7fa01 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -52,7 +52,7 @@ import { readCssFile } from './util/css'
import { ProjectLocator, type ProjectConfig } from './project-locator'
import type { TailwindCssSettings } from '@tailwindcss/language-service/src/util/state'
import { createResolver, Resolver } from './resolver'
-import { retry } from './util/retry'
+import { analyzeStylesheet } from './version-guesser.js'
const TRIGGER_CHARACTERS = [
// class attributes
@@ -382,6 +382,13 @@ export class TW {
for (let [, project] of this.projects) {
if (!project.state.v4) continue
+ if (
+ change.type === FileChangeType.Deleted &&
+ changeAffectsFile(normalizedFilename, [project.projectConfig.configPath])
+ ) {
+ continue
+ }
+
if (!changeAffectsFile(normalizedFilename, project.dependencies())) continue
needsSoftRestart = true
@@ -405,6 +412,31 @@ export class TW {
needsRestart = true
break
}
+
+ //
+ else {
+ // If the main CSS file in a project is deleted and then re-created
+ // the server won't restart because the project is gone by now and
+ // there's no concept of a "config file" for us to compare with
+ //
+ // So we'll check if the stylesheet could *potentially* create
+ // a new project but we'll only do so if no projects were found
+ //
+ // If we did this all the time we'd potentially restart the server
+ // unncessarily a lot while the user is editing their stylesheets
+ if (this.projects.size > 0) continue
+
+ let content = await readCssFile(change.file)
+ if (!content) continue
+
+ let stylesheet = analyzeStylesheet(content)
+ if (!stylesheet.root) continue
+
+ if (!stylesheet.versions.includes('4')) continue
+
+ needsRestart = true
+ break
+ }
}
let isConfigFile = isConfigMatcher(normalizedFilename)
@@ -1041,11 +1073,17 @@ export class TW {
this.watched.length = 0
}
- restart(): void {
+ async restart(): void {
+ let isTestMode = this.initializeParams.initializationOptions?.testMode ?? false
+
console.log('----------\nRESTARTING\n----------')
this.dispose()
this.initPromise = undefined
- this.init()
+ await this.init()
+
+ if (isTestMode) {
+ this.connection.sendNotification('@/tailwindCSS/serverRestarted')
+ }
}
async softRestart(): Promise {
diff --git a/packages/tailwindcss-language-server/tests/env/restart.test.ts b/packages/tailwindcss-language-server/tests/env/restart.test.ts
new file mode 100644
index 00000000..a973b2e0
--- /dev/null
+++ b/packages/tailwindcss-language-server/tests/env/restart.test.ts
@@ -0,0 +1,169 @@
+import { expect } from 'vitest'
+import * as fs from 'node:fs/promises'
+import * as path from 'node:path'
+import { css, defineTest } from '../../src/testing'
+import dedent from 'dedent'
+import { createClient } from '../utils/client'
+
+defineTest({
+ name: 'The design system is reloaded when the CSS changes ($watcher)',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+
+ @theme {
+ --color-primary: #c0ffee;
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ capabilities(caps) {
+ caps.workspace!.didChangeWatchedFiles!.dynamicRegistration = false
+ },
+ }),
+ }),
+ handle: async ({ root, client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ //
+ // ^
+ let hover = await doc.hover({ line: 0, character: 13 })
+
+ expect(hover).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .text-primary {
+ color: var(--color-primary) /* #c0ffee */;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 24 },
+ },
+ })
+
+ let didReload = new Promise((resolve) => {
+ client.conn.onNotification('@/tailwindCSS/projectReloaded', resolve)
+ })
+
+ // Update the CSS
+ await fs.writeFile(
+ path.resolve(root, 'app.css'),
+ css`
+ @import 'tailwindcss';
+
+ @theme {
+ --color-primary: #bada55;
+ }
+ `,
+ )
+
+ await didReload
+
+ //
+ // ^
+ let hover2 = await doc.hover({ line: 0, character: 13 })
+
+ expect(hover2).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .text-primary {
+ color: var(--color-primary) /* #bada55 */;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 24 },
+ },
+ })
+ },
+})
+
+defineTest({
+ options: {
+ retry: 3,
+ },
+ name: 'Server is "restarted" when a config file is removed',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+
+ @theme {
+ --color-primary: #c0ffee;
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ capabilities(caps) {
+ caps.workspace!.didChangeWatchedFiles!.dynamicRegistration = false
+ },
+ }),
+ }),
+ handle: async ({ root, client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let hover = await doc.hover({ line: 0, character: 13 })
+
+ expect(hover).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .text-primary {
+ color: var(--color-primary) /* #c0ffee */;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 24 },
+ },
+ })
+
+ // Remove the CSS file
+ let didRestart = new Promise((resolve) => {
+ client.conn.onNotification('@/tailwindCSS/serverRestarted', resolve)
+ })
+ await fs.unlink(path.resolve(root, 'app.css'))
+ await didRestart
+
+ //
+ // ^
+ let hover2 = await doc.hover({ line: 0, character: 13 })
+ expect(hover2).toEqual(null)
+
+ // Re-create the CSS file
+ let didRestartAgain = new Promise((resolve) => {
+ client.conn.onNotification('@/tailwindCSS/serverRestarted', resolve)
+ })
+ await fs.writeFile(
+ path.resolve(root, 'app.css'),
+ css`
+ @import 'tailwindcss';
+ `,
+ )
+ await didRestartAgain
+
+ await new Promise((resolve) => setTimeout(resolve, 500))
+
+ //
+ // ^
+ let hover3 = await doc.hover({ line: 0, character: 13 })
+ expect(hover3).toEqual(null)
+ },
+})
diff --git a/packages/tailwindcss-language-server/tests/utils/client.ts b/packages/tailwindcss-language-server/tests/utils/client.ts
index 3a860230..760b805b 100644
--- a/packages/tailwindcss-language-server/tests/utils/client.ts
+++ b/packages/tailwindcss-language-server/tests/utils/client.ts
@@ -6,7 +6,6 @@ import {
CompletionList,
CompletionParams,
Diagnostic,
- DidChangeWorkspaceFoldersNotification,
Disposable,
DocumentLink,
DocumentLinkRequest,
@@ -179,6 +178,11 @@ export interface ClientOptions extends ConnectOptions {
* and the Tailwind CSS version it detects
*/
features?: Feature[]
+
+ /**
+ * Tweak the client capabilities presented to the server
+ */
+ capabilities?(caps: ClientCapabilities): ClientCapabilities | Promise
| void
}
export interface Client extends ClientWorkspace {
@@ -394,6 +398,8 @@ export async function createClient(opts: ClientOptions): Promise {
},
}
+ capabilities = (await opts.capabilities?.(capabilities)) ?? capabilities
+
trace('Client initializing')
await conn.sendRequest(InitializeRequest.type, {
processId: process.pid,
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 1750eb69..7a59b6c4 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,7 @@
- Only scan the file system once when needed ([#1287](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1287))
- Don't follow recursive symlinks when searching for projects ([#1270](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1270))
+- Correctly re-create a project when its main config file is removed then re-created ([#1300](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1300))
# 0.14.13
From 3f6cc1f30af3915538489fa687cb283a6d10a10f Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 14:14:47 -0400
Subject: [PATCH 049/108] Bump `@parcel/watcher` (#1269)
This is just a general version bump. I thought an issue was parcel
watcher related but turned out not to be. Saw we were **very** behind so
I wanted to make sure we were up to date.
---
.../tailwindcss-language-server/package.json | 10 +-
.../tailwindcss-language-server/src/tw.ts | 8 +
.../src/watcher/index.js | 21 +-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
pnpm-lock.yaml | 182 ++++++++++++++++--
5 files changed, 196 insertions(+), 26 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index c4dc2984..55c890a4 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -34,7 +34,15 @@
"access": "public"
},
"devDependencies": {
- "@parcel/watcher": "2.0.3",
+ "@parcel/watcher": "2.5.1",
+ "@parcel/watcher-darwin-x64": "2.5.1",
+ "@parcel/watcher-darwin-arm64": "2.5.1",
+ "@parcel/watcher-win32-x64": "2.5.1",
+ "@parcel/watcher-win32-arm64": "2.5.1",
+ "@parcel/watcher-linux-x64-glibc": "2.5.1",
+ "@parcel/watcher-linux-x64-musl": "2.5.1",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm64-musl": "2.5.1",
"@tailwindcss/aspect-ratio": "0.4.2",
"@tailwindcss/container-queries": "0.1.0",
"@tailwindcss/forms": "0.5.3",
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index a0b7fa01..682aefcd 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -521,6 +521,10 @@ export class TW {
}
}
} else if (parcel.getBinding()) {
+ console.log(
+ '[Global] Your LSP client does not support watching files on behalf of the server',
+ )
+ console.log('[Global] Using bundled file watcher: @parcel/watcher')
let typeMap = {
create: FileChangeType.Created,
update: FileChangeType.Changed,
@@ -547,6 +551,10 @@ export class TW {
},
})
} else {
+ console.log(
+ '[Global] Your LSP client does not support watching files on behalf of the server',
+ )
+ console.log('[Global] Using bundled file watcher: chokidar')
let watch: typeof chokidar.watch = require('chokidar').watch
let chokidarWatcher = watch(
[`**/${CONFIG_GLOB}`, `**/${PACKAGE_LOCK_GLOB}`, `**/${CSS_GLOB}`, `**/${TSCONFIG_GLOB}`],
diff --git a/packages/tailwindcss-language-server/src/watcher/index.js b/packages/tailwindcss-language-server/src/watcher/index.js
index ecf582e6..46cec8e8 100644
--- a/packages/tailwindcss-language-server/src/watcher/index.js
+++ b/packages/tailwindcss-language-server/src/watcher/index.js
@@ -13,21 +13,24 @@ const uv = (process.versions.uv || '').split('.')[0]
const prebuilds = {
'darwin-arm64': {
- 'node.napi.glibc.node': () =>
- require('@parcel/watcher/prebuilds/darwin-arm64/node.napi.glibc.node'),
+ 'node.napi.glibc.node': () => require('@parcel/watcher-darwin-arm64/watcher.node'),
},
'darwin-x64': {
- 'node.napi.glibc.node': () =>
- require('@parcel/watcher/prebuilds/darwin-x64/node.napi.glibc.node'),
+ 'node.napi.glibc.node': () => require('@parcel/watcher-darwin-x64/watcher.node'),
},
'linux-x64': {
- 'node.napi.glibc.node': () =>
- require('@parcel/watcher/prebuilds/linux-x64/node.napi.glibc.node'),
- 'node.napi.musl.node': () => require('@parcel/watcher/prebuilds/linux-x64/node.napi.musl.node'),
+ 'node.napi.glibc.node': () => require('@parcel/watcher-linux-x64-glibc/watcher.node'),
+ 'node.napi.musl.node': () => require('@parcel/watcher-linux-x64-musl/watcher.node'),
+ },
+ 'linux-arm64': {
+ 'node.napi.glibc.node': () => require('@parcel/watcher-linux-arm64-glibc/watcher.node'),
+ 'node.napi.musl.node': () => require('@parcel/watcher-linux-arm64-musl/watcher.node'),
},
'win32-x64': {
- 'node.napi.glibc.node': () =>
- require('@parcel/watcher/prebuilds/win32-x64/node.napi.glibc.node'),
+ 'node.napi.glibc.node': () => require('@parcel/watcher-win32-x64/watcher.node'),
+ },
+ 'win32-arm64': {
+ 'node.napi.glibc.node': () => require('@parcel/watcher-win32-arm64/watcher.node'),
},
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 7a59b6c4..599345e6 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -5,6 +5,7 @@
- Only scan the file system once when needed ([#1287](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1287))
- Don't follow recursive symlinks when searching for projects ([#1270](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1270))
- Correctly re-create a project when its main config file is removed then re-created ([#1300](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1300))
+- Bump `@parcel/watcher` used by the language server ([#1269](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1269))
# 0.14.13
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c4e0edaf..2255d6ad 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -33,8 +33,32 @@ importers:
packages/tailwindcss-language-server:
devDependencies:
'@parcel/watcher':
- specifier: 2.0.3
- version: 2.0.3
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-darwin-arm64':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-darwin-x64':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-linux-arm64-glibc':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-linux-arm64-musl':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-linux-x64-glibc':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-linux-x64-musl':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-win32-arm64':
+ specifier: 2.5.1
+ version: 2.5.1
+ '@parcel/watcher-win32-x64':
+ specifier: 2.5.1
+ version: 2.5.1
'@tailwindcss/aspect-ratio':
specifier: 0.4.2
version: 0.4.2(tailwindcss@3.4.17)
@@ -767,8 +791,86 @@ packages:
resolution: {integrity: sha512-wBqcGsMELZna0jDblGd7UXgOby45TQaMWmbFwWX+SEotk4HV6zG2t6rT9siyLhPk4P6YYqgfL1UO8nMWDBVJXQ==}
engines: {node: ^16.14.0 || >=18.0.0}
- '@parcel/watcher@2.0.3':
- resolution: {integrity: sha512-PHh5PArr3nYGYVj9z/NSfDmmKEBNrg2bzoFgxzjTRBBxPUKx039x3HF6VGLFIfrghjJxcYn/IeSpdVwfob7KFA==}
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
engines: {node: '>= 10.0.0'}
'@pkgjs/parseargs@0.11.0':
@@ -1372,6 +1474,11 @@ packages:
resolution: {integrity: sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==}
engines: {node: '>=8'}
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
detect-libc@2.0.3:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
@@ -1910,15 +2017,11 @@ packages:
resolution: {integrity: sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==}
engines: {node: '>=10'}
- node-addon-api@3.2.1:
- resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
-
node-addon-api@4.3.0:
resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==}
- node-gyp-build@4.8.1:
- resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==}
- hasBin: true
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-releases@2.0.18:
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
@@ -3027,10 +3130,57 @@ snapshots:
dependencies:
which: 4.0.0
- '@parcel/watcher@2.0.3':
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1': {}
+
+ '@parcel/watcher-darwin-x64@2.5.1': {}
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1': {}
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1': {}
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1': {}
+
+ '@parcel/watcher-linux-x64-musl@2.5.1': {}
+
+ '@parcel/watcher-win32-arm64@2.5.1': {}
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1': {}
+
+ '@parcel/watcher@2.5.1':
dependencies:
- node-addon-api: 3.2.1
- node-gyp-build: 4.8.1
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -3580,6 +3730,8 @@ snapshots:
detect-indent@6.0.0: {}
+ detect-libc@1.0.3: {}
+
detect-libc@2.0.3:
optional: true
@@ -4122,12 +4274,10 @@ snapshots:
semver: 7.7.1
optional: true
- node-addon-api@3.2.1: {}
-
node-addon-api@4.3.0:
optional: true
- node-gyp-build@4.8.1: {}
+ node-addon-api@7.1.1: {}
node-releases@2.0.18: {}
From 86bfb7d53a52422c766ac1348a9c953576d6cbdf Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 14:20:09 -0400
Subject: [PATCH 050/108] Fix type error
---
packages/tailwindcss-language-server/src/tw.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index 682aefcd..20ac0158 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -1081,7 +1081,7 @@ export class TW {
this.watched.length = 0
}
- async restart(): void {
+ async restart(): Promise {
let isTestMode = this.initializeParams.initializationOptions?.testMode ?? false
console.log('----------\nRESTARTING\n----------')
From e24050ef98ddb4fea4fcd77dfab0764e3e69320e Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 16:34:18 -0400
Subject: [PATCH 051/108] Skip flaky test
---
.../tailwindcss-language-server/tests/env/restart.test.ts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/tailwindcss-language-server/tests/env/restart.test.ts b/packages/tailwindcss-language-server/tests/env/restart.test.ts
index a973b2e0..49b632c3 100644
--- a/packages/tailwindcss-language-server/tests/env/restart.test.ts
+++ b/packages/tailwindcss-language-server/tests/env/restart.test.ts
@@ -91,6 +91,11 @@ defineTest({
defineTest({
options: {
retry: 3,
+
+ // This test passes on all platforms but it is super flaky
+ // The server needs some re-working to ensure everything is awaited
+ // properly with respect to messages and server responses
+ skip: true,
},
name: 'Server is "restarted" when a config file is removed',
fs: {
From 933c968eb26d908334ed4ca325809c85727a9004 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 7 Apr 2025 16:43:56 -0400
Subject: [PATCH 052/108] 0.14.14
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 55c890a4..971c2aaf 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.13",
+ "version": "0.14.14",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 23907838..79f5e818 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.13",
+ "version": "0.14.14",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 599345e6..fa0302ae 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.14
+
- Only scan the file system once when needed ([#1287](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1287))
- Don't follow recursive symlinks when searching for projects ([#1270](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1270))
- Correctly re-create a project when its main config file is removed then re-created ([#1300](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1300))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 4f55fb4a..2ac8c102 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.13",
+ "version": "0.14.14",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 8f5d895f32ca7c416db9a615ea150c51b240fb13 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 9 Apr 2025 07:13:05 -0400
Subject: [PATCH 053/108] Fix infinite loop when any file exclusions start with
`/` (#1307)
See https://github.com/SuperchupuDev/tinyglobby/issues/99
Fixes #1303
---
.../src/project-locator.test.ts | 39 ++++++++++++++++++-
.../src/project-locator.ts | 13 ++++++-
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts
index f43479ae..3d5cddbb 100644
--- a/packages/tailwindcss-language-server/src/project-locator.test.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.test.ts
@@ -462,6 +462,43 @@ testLocator({
],
})
+testLocator({
+ name: 'File exclusions starting with `/` do not cause traversal to loop forever',
+ fs: {
+ 'index.css': css`
+ @import 'tailwindcss';
+ `,
+ 'vendor/a.css': css`
+ @import 'tailwindcss';
+ `,
+ 'vendor/nested/b.css': css`
+ @import 'tailwindcss';
+ `,
+ 'src/vendor/c.css': css`
+ @import 'tailwindcss';
+ `,
+ },
+ settings: {
+ tailwindCSS: {
+ files: {
+ exclude: ['/vendor'],
+ },
+ } as Settings['tailwindCSS'],
+ },
+ expected: [
+ {
+ version: '4.1.1 (bundled)',
+ config: '/index.css',
+ content: [],
+ },
+ {
+ version: '4.1.1 (bundled)',
+ config: '/src/vendor/c.css',
+ content: [],
+ },
+ ],
+})
+
// ---
function testLocator({
@@ -502,7 +539,7 @@ function testLocator({
})
}
-async function prepare({ root }: TestUtils) {
+async function prepare({ root }: TestUtils) {
let defaultSettings = {
tailwindCSS: {
files: {
diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts
index de562b63..4e3c0453 100644
--- a/packages/tailwindcss-language-server/src/project-locator.ts
+++ b/packages/tailwindcss-language-server/src/project-locator.ts
@@ -274,11 +274,22 @@ export class ProjectLocator {
}
private async findConfigs(): Promise {
+ let ignore = this.settings.tailwindCSS.files.exclude
+
+ // NOTE: This is a temporary workaround for a bug in the `fdir` package used
+ // by `tinyglobby`. It infinite loops when the ignore pattern starts with
+ // a `/`. This should be removed once the bug is fixed.
+ ignore = ignore.map((pattern) => {
+ if (!pattern.startsWith('/')) return pattern
+
+ return pattern.slice(1)
+ })
+
// Look for config files and CSS files
let files = await glob({
patterns: [`**/${CONFIG_GLOB}`, `**/${CSS_GLOB}`],
cwd: this.base,
- ignore: this.settings.tailwindCSS.files.exclude,
+ ignore,
onlyFiles: true,
absolute: true,
followSymbolicLinks: true,
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index fa0302ae..e9a9bbde 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Prevent infinite loop when any file exclusion starts with `/` ([#1307](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1307))
# 0.14.14
From d561b6149804f0c313ddebb6a4bd90769d33d272 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Tue, 8 Apr 2025 23:30:54 -0400
Subject: [PATCH 054/108] 0.14.15
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 971c2aaf..0813d13e 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.14",
+ "version": "0.14.15",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 79f5e818..2a1e3079 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.14",
+ "version": "0.14.15",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index e9a9bbde..17f07afc 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.15
+
- Prevent infinite loop when any file exclusion starts with `/` ([#1307](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1307))
# 0.14.14
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 2ac8c102..26952a45 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.14",
+ "version": "0.14.15",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From 92ea52fa3c60c9e6cdc1cd27ae35502d34be7634 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 14 Apr 2025 11:04:45 -0400
Subject: [PATCH 055/108] Warn when using blocklisted classes (#1310)
---
.../src/projects.ts | 6 ++-
.../tests/diagnostics/diagnostics.test.js | 44 ++++++++++++++++++-
.../src/diagnostics/diagnosticsProvider.ts | 5 +++
.../getUsedBlocklistedClassDiagnostics.ts | 41 +++++++++++++++++
.../src/diagnostics/types.ts | 12 +++++
.../src/util/state.ts | 2 +
.../src/util/v4/design-system.ts | 1 +
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
packages/vscode-tailwindcss/README.md | 4 ++
packages/vscode-tailwindcss/package.json | 11 +++++
10 files changed, 125 insertions(+), 3 deletions(-)
create mode 100644 packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts
diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index 25aacd5f..04160569 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -837,6 +837,7 @@ export async function createProjectService(
)
state.designSystem = designSystem
+ state.blocklist = Array.from(designSystem.invalidCandidates ?? [])
let deps = designSystem.dependencies()
@@ -982,7 +983,9 @@ export async function createProjectService(
if (typeof state.separator !== 'string') {
state.separator = ':'
}
- state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : []
+ if (!state.v4) {
+ state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : []
+ }
delete state.config.blocklist
if (state.v4) {
@@ -1148,6 +1151,7 @@ export async function createProjectService(
state.designSystem = designSystem
state.classList = classList
state.variants = getVariants(state)
+ state.blocklist = Array.from(designSystem.invalidCandidates ?? [])
let deps = designSystem.dependencies()
diff --git a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
index 56fe9f7f..afda8837 100644
--- a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
+++ b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
@@ -1,6 +1,8 @@
+import * as fs from 'node:fs/promises'
import { expect, test } from 'vitest'
import { withFixture } from '../common'
-import * as fs from 'node:fs/promises'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
withFixture('basic', (c) => {
function testFixture(fixture) {
@@ -383,3 +385,43 @@ withFixture('v4/basic', (c) => {
],
})
})
+
+defineTest({
+ name: 'Shows warning when using blocklisted classes',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ @source not inline("{,hover:}flex");
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ let diagnostics = await doc.diagnostics()
+
+ expect(diagnostics).toEqual([
+ {
+ code: 'usedBlocklistedClass',
+ message: 'The class "flex" will not be generated as it has been blocklisted',
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 16 },
+ },
+ severity: 2,
+ },
+ {
+ code: 'usedBlocklistedClass',
+ message: 'The class "hover:flex" will not be generated as it has been blocklisted',
+ range: {
+ start: { line: 0, character: 27 },
+ end: { line: 0, character: 37 },
+ },
+ severity: 2,
+ },
+ ])
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
index 18994526..075cea38 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
@@ -9,6 +9,7 @@ import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnosti
import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics'
import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics'
import { getInvalidSourceDiagnostics } from './getInvalidSourceDiagnostics'
+import { getUsedBlocklistedClassDiagnostics } from './getUsedBlocklistedClassDiagnostics'
export async function doValidate(
state: State,
@@ -22,6 +23,7 @@ export async function doValidate(
DiagnosticKind.InvalidTailwindDirective,
DiagnosticKind.InvalidSourceDirective,
DiagnosticKind.RecommendedVariantOrder,
+ DiagnosticKind.UsedBlocklistedClass,
],
): Promise
{
const settings = await state.editor.getConfiguration(document.uri)
@@ -52,6 +54,9 @@ export async function doValidate(
...(only.includes(DiagnosticKind.RecommendedVariantOrder)
? await getRecommendedVariantOrderDiagnostics(state, document, settings)
: []),
+ ...(only.includes(DiagnosticKind.UsedBlocklistedClass)
+ ? await getUsedBlocklistedClassDiagnostics(state, document, settings)
+ : []),
]
: []
}
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts
new file mode 100644
index 00000000..07f1084c
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts
@@ -0,0 +1,41 @@
+import type { TextDocument } from 'vscode-languageserver-textdocument'
+import type { State, Settings } from '../util/state'
+import { type UsedBlocklistedClassDiagnostic, DiagnosticKind } from './types'
+import { findClassListsInDocument, getClassNamesInClassList } from '../util/find'
+
+export async function getUsedBlocklistedClassDiagnostics(
+ state: State,
+ document: TextDocument,
+ settings: Settings,
+): Promise {
+ if (!state.v4) return []
+ if (!state.blocklist?.length) return []
+
+ let severity = settings.tailwindCSS.lint.usedBlocklistedClass
+ if (severity === 'ignore') return []
+
+ let blocklist = new Set(state.blocklist ?? [])
+ let diagnostics: UsedBlocklistedClassDiagnostic[] = []
+
+ let classLists = await findClassListsInDocument(state, document)
+
+ for (let classList of classLists) {
+ let classNames = getClassNamesInClassList(classList, [])
+
+ for (let className of classNames) {
+ if (!blocklist.has(className.className)) continue
+
+ diagnostics.push({
+ code: DiagnosticKind.UsedBlocklistedClass,
+ range: className.range,
+ severity:
+ severity === 'error'
+ ? 1 /* DiagnosticSeverity.Error */
+ : 2 /* DiagnosticSeverity.Warning */,
+ message: `The class "${className.className}" will not be generated as it has been blocklisted`,
+ })
+ }
+ }
+
+ return diagnostics
+}
diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts
index 7cb68a7e..f022503a 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/types.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts
@@ -10,6 +10,7 @@ export enum DiagnosticKind {
InvalidTailwindDirective = 'invalidTailwindDirective',
InvalidSourceDirective = 'invalidSourceDirective',
RecommendedVariantOrder = 'recommendedVariantOrder',
+ UsedBlocklistedClass = 'usedBlocklistedClass',
}
export type CssConflictDiagnostic = Diagnostic & {
@@ -100,6 +101,16 @@ export function isRecommendedVariantOrderDiagnostic(
return diagnostic.code === DiagnosticKind.RecommendedVariantOrder
}
+export type UsedBlocklistedClassDiagnostic = Diagnostic & {
+ code: DiagnosticKind.UsedBlocklistedClass
+}
+
+export function isUsedBlocklistedClass(
+ diagnostic: AugmentedDiagnostic,
+): diagnostic is UsedBlocklistedClassDiagnostic {
+ return diagnostic.code === DiagnosticKind.UsedBlocklistedClass
+}
+
export type AugmentedDiagnostic =
| CssConflictDiagnostic
| InvalidApplyDiagnostic
@@ -109,3 +120,4 @@ export type AugmentedDiagnostic =
| InvalidTailwindDirectiveDiagnostic
| InvalidSourceDirectiveDiagnostic
| RecommendedVariantOrderDiagnostic
+ | UsedBlocklistedClassDiagnostic
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 8c819eb8..754f2a49 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -65,6 +65,7 @@ export type TailwindCssSettings = {
invalidTailwindDirective: DiagnosticSeveritySetting
invalidSourceDirective: DiagnosticSeveritySetting
recommendedVariantOrder: DiagnosticSeveritySetting
+ usedBlocklistedClass: DiagnosticSeveritySetting
}
experimental: {
classRegex: string[] | [string, string][]
@@ -203,6 +204,7 @@ export function getDefaultTailwindSettings(): Settings {
invalidTailwindDirective: 'error',
invalidSourceDirective: 'error',
recommendedVariantOrder: 'warning',
+ usedBlocklistedClass: 'warning',
},
showPixelEquivalents: true,
includeLanguages: {},
diff --git a/packages/tailwindcss-language-service/src/util/v4/design-system.ts b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
index 20b7ff32..13c657c3 100644
--- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
@@ -40,6 +40,7 @@ export interface DesignSystem {
// Optional because it did not exist in earlier v4 alpha versions
resolveThemeValue?(path: string, forceInline?: boolean): string | undefined
+ invalidCandidates?: Set
}
export interface DesignSystem {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 17f07afc..4c9240f6 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
# 0.14.15
diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md
index efc937cc..f74b1407 100644
--- a/packages/vscode-tailwindcss/README.md
+++ b/packages/vscode-tailwindcss/README.md
@@ -172,6 +172,10 @@ Class names on the same HTML element which apply the same CSS property or proper
Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only). **Default: `warning`**
+#### `tailwindCSS.lint.usedBlocklistedClass`
+
+Usage of class names that have been blocklisted via `@source not inline(…)`. **Default: `warning`**
+
### `tailwindCSS.inspectPort`
Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`**
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 26952a45..d3d30911 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -305,6 +305,17 @@
"markdownDescription": "Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only)",
"scope": "language-overridable"
},
+ "tailwindCSS.lint.usedBlocklistedClass": {
+ "type": "string",
+ "enum": [
+ "ignore",
+ "warning",
+ "error"
+ ],
+ "default": "warning",
+ "markdownDescription": "Usage of class names that have been blocklisted via `@source not inline(…)`",
+ "scope": "language-overridable"
+ },
"tailwindCSS.experimental.classRegex": {
"type": "array",
"scope": "language-overridable"
From bd1fc8c4f52aa0baddc92a5710b649bfd5bb953d Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 14 Apr 2025 11:14:56 -0400
Subject: [PATCH 056/108] Add test for `classAttributes` matching variable
names in JS(X)/TS(X) (#1315)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Welp… this was 100% a bug that this behavior got introduced but at this
point it’s shipped and people might rely on it or otherwise want this
behavior. As such I’m explicitly encoding this in a test so it doesn’t
break later when the big parsing refactor lands.
---
.../src/util/find.test.ts | 111 +++++++++++++++++-
.../src/util/test-utils.ts | 1 +
2 files changed, 111 insertions(+), 1 deletion(-)
diff --git a/packages/tailwindcss-language-service/src/util/find.test.ts b/packages/tailwindcss-language-service/src/util/find.test.ts
index 561f82e8..839fb6d0 100644
--- a/packages/tailwindcss-language-service/src/util/find.test.ts
+++ b/packages/tailwindcss-language-service/src/util/find.test.ts
@@ -1,6 +1,6 @@
import { test } from 'vitest'
import { findClassListsInHtmlRange } from './find'
-import { js, html, createDocument } from './test-utils'
+import { js, html, pug, createDocument } from './test-utils'
test('class regex works in astro', async ({ expect }) => {
let file = createDocument({
@@ -682,3 +682,112 @@ test('classFunctions should only match in JS-like contexts', async ({ expect })
},
])
})
+
+test('classAttributes find class lists inside variables in JS(X)/TS(X)', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.html',
+ lang: 'javascript',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['className'],
+ },
+ },
+ content: js`
+ let className = {
+ a: "relative flex",
+ nested: {
+ b: "relative flex",
+ },
+ inFn: fn({
+ c: "relative flex",
+ })
+ }
+
+ let other = {
+ a: "relative flex",
+ }
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 1, character: 6 },
+ end: { line: 1, character: 19 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 3, character: 8 },
+ end: { line: 3, character: 21 },
+ },
+ },
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 6, character: 8 },
+ end: { line: 6, character: 21 },
+ },
+ },
+ ])
+})
+
+test('classAttributes find class lists inside pug', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.pug',
+ lang: 'pug',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['className'],
+ },
+ },
+ content: pug`
+ div(classNAme="relative flex")
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 0, character: 15 },
+ end: { line: 0, character: 28 },
+ },
+ },
+ ])
+})
+
+test('classAttributes find class lists inside Vue bindings', async ({ expect }) => {
+ let file = createDocument({
+ name: 'file.pug',
+ lang: 'vue',
+ settings: {
+ tailwindCSS: {
+ classAttributes: ['class'],
+ },
+ },
+ content: html`
+
+
+
+ `,
+ })
+
+ let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'js')
+
+ expect(classLists).toEqual([
+ {
+ classList: 'relative flex',
+ range: {
+ start: { line: 1, character: 17 },
+ end: { line: 1, character: 30 },
+ },
+ },
+ ])
+})
diff --git a/packages/tailwindcss-language-service/src/util/test-utils.ts b/packages/tailwindcss-language-service/src/util/test-utils.ts
index 088dade5..ce66ecaf 100644
--- a/packages/tailwindcss-language-service/src/util/test-utils.ts
+++ b/packages/tailwindcss-language-service/src/util/test-utils.ts
@@ -9,6 +9,7 @@ export const ts = dedent
export const tsx = dedent
export const css = dedent
export const html = dedent
+export const pug = dedent
export function createDocument({
name,
From e88d6dcb78f493ef56e374e6b2195bec3082165a Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 23 Apr 2025 10:51:23 -0400
Subject: [PATCH 057/108] Support class function hovers in Svelte and HTML
`` works
---
.../src/util/find.test.ts | 86 ++++++++++++++++++-
.../src/util/find.ts | 4 +-
.../src/util/state.ts | 1 +
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
4 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-service/src/util/find.test.ts b/packages/tailwindcss-language-service/src/util/find.test.ts
index 839fb6d0..170217c0 100644
--- a/packages/tailwindcss-language-service/src/util/find.test.ts
+++ b/packages/tailwindcss-language-service/src/util/find.test.ts
@@ -1,5 +1,5 @@
import { test } from 'vitest'
-import { findClassListsInHtmlRange } from './find'
+import { findClassListsInHtmlRange, findClassNameAtPosition } from './find'
import { js, html, pug, createDocument } from './test-utils'
test('class regex works in astro', async ({ expect }) => {
@@ -791,3 +791,87 @@ test('classAttributes find class lists inside Vue bindings', async ({ expect })
},
])
})
+
+test('Can find class name inside JS/TS functions in
+ `,
+ })
+
+ let className = await findClassNameAtPosition(file.state, file.doc, {
+ line: 1,
+ character: 23,
+ })
+
+ expect(className).toEqual({
+ className: 'flex',
+ range: {
+ start: { line: 1, character: 22 },
+ end: { line: 1, character: 26 },
+ },
+ relativeRange: {
+ start: { line: 0, character: 0 },
+ end: { line: 0, character: 4 },
+ },
+ classList: {
+ classList: 'flex relative',
+ important: undefined,
+ range: {
+ start: { character: 22, line: 1 },
+ end: { character: 35, line: 1 },
+ },
+ },
+ })
+})
+
+test('Can find class name inside JS/TS functions in
+ `,
+ })
+
+ let className = await findClassNameAtPosition(file.state, file.doc, {
+ line: 1,
+ character: 23,
+ })
+
+ expect(className).toEqual({
+ className: 'flex',
+ range: {
+ start: { line: 1, character: 22 },
+ end: { line: 1, character: 26 },
+ },
+ relativeRange: {
+ start: { line: 0, character: 0 },
+ end: { line: 0, character: 4 },
+ },
+ classList: {
+ classList: 'flex relative',
+ important: undefined,
+ range: {
+ start: { character: 22, line: 1 },
+ end: { character: 35, line: 1 },
+ },
+ },
+ })
+})
diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts
index 9118403d..53ad7195 100644
--- a/packages/tailwindcss-language-service/src/util/find.ts
+++ b/packages/tailwindcss-language-service/src/util/find.ts
@@ -5,7 +5,7 @@ import lineColumn from 'line-column'
import { isCssContext, isCssDoc } from './css'
import { isHtmlContext, isVueDoc } from './html'
import { isWithinRange } from './isWithinRange'
-import { isJsxContext } from './js'
+import { isJsContext } from './js'
import { dedupeByRange, flatten } from './array'
import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers'
import { getLanguageBoundaries } from './getLanguageBoundaries'
@@ -526,7 +526,7 @@ export async function findClassNameAtPosition(
classNames = await findClassNamesInRange(state, doc, searchRange, 'css')
} else if (isHtmlContext(state, doc, position)) {
classNames = await findClassNamesInRange(state, doc, searchRange, 'html')
- } else if (isJsxContext(state, doc, position)) {
+ } else if (isJsContext(state, doc, position)) {
classNames = await findClassNamesInRange(state, doc, searchRange, 'jsx')
} else {
classNames = await findClassNamesInRange(state, doc, searchRange)
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 754f2a49..26cc2ed0 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -228,6 +228,7 @@ export function createState(
return {
enabled: true,
features: [],
+ blocklist: [],
...partial,
editor: {
get connection(): Connection {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 4c9240f6..4162495c 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,7 @@
## Prerelease
- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
+- Support class function hovers in Svelte and HTML `` blocks ([#1311](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1311))
# 0.14.15
From 4fb66e12cc2beb30315ba0d883a14ed173cbd589 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 23 Apr 2025 11:12:18 -0400
Subject: [PATCH 058/108] =?UTF-8?q?Implement=20better=20`calc(=E2=80=A6)`?=
=?UTF-8?q?=20evaluation=20for=20completions=20and=20equivalents=20(#1316)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes #1314
- [x] Investigate the change to the TS config as I feel like it should
not be necessary
- [x] Manually verify the stated case from the issue above is addressed
Hovers:
Completions:
---
.../tailwindcss-language-service/package.json | 5 +-
.../src/util/rewriting/calc.ts | 66 ++++++-------------
.../src/util/rewriting/index.test.ts | 2 +
.../tsconfig.json | 4 +-
pnpm-lock.yaml | 59 +++++++++--------
5 files changed, 60 insertions(+), 76 deletions(-)
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 2a1e3079..4a11e6c7 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -13,8 +13,9 @@
"test": "vitest"
},
"dependencies": {
- "@csstools/css-parser-algorithms": "2.1.1",
- "@csstools/css-tokenizer": "2.1.1",
+ "@csstools/css-calc": "2.1.2",
+ "@csstools/css-parser-algorithms": "3.0.4",
+ "@csstools/css-tokenizer": "3.0.3",
"@csstools/media-query-list-parser": "2.0.4",
"@types/culori": "^2.1.0",
"@types/moo": "0.5.3",
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/calc.ts b/packages/tailwindcss-language-service/src/util/rewriting/calc.ts
index 2420800f..e9949014 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/calc.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/calc.ts
@@ -1,56 +1,30 @@
-function parseLength(length: string): [number, string] | null {
- let regex = /^(-?\d*\.?\d+)([a-z%]*)$/i
- let match = length.match(regex)
-
- if (!match) return null
-
- let numberPart = parseFloat(match[1])
- if (isNaN(numberPart)) return null
-
- return [numberPart, match[2]]
-}
-
-function round(n: number, precision: number): number {
- return Math.round(n * Math.pow(10, precision)) / Math.pow(10, precision)
-}
+import { stringify, tokenize } from '@csstools/css-tokenizer'
+import { isFunctionNode, parseComponentValue } from '@csstools/css-parser-algorithms'
+import { calcFromComponentValues } from '@csstools/css-calc'
export function evaluateExpression(str: string): string | null {
- // We're only interested simple calc expressions of the form
- // A + B, A - B, A * B, A / B
+ let tokens = tokenize({ css: `calc(${str})` })
- let parts = str.split(/\s+([+*/-])\s+/)
+ let components = parseComponentValue(tokens, {})
+ if (!components) return null
- if (parts.length === 1) return null
- if (parts.length !== 3) return null
+ let result = calcFromComponentValues([[components]], {
+ // Ensure evaluation of random() is deterministic
+ randomSeed: 1,
- let a = parseLength(parts[0])
- let b = parseLength(parts[2])
+ // Limit precision to keep values environment independent
+ precision: 4,
+ })
- // Not parsable
- if (!a || !b) {
- return null
- }
-
- // Addition and subtraction require the same units
- if ((parts[1] === '+' || parts[1] === '-') && a[1] !== b[1]) {
- return null
- }
-
- // Multiplication and division require at least one unit to be empty
- if ((parts[1] === '*' || parts[1] === '/') && a[1] !== '' && b[1] !== '') {
- return null
- }
+ // The result array is the same shape as the original so we're guaranteed to
+ // have an element here
+ let node = result[0][0]
- switch (parts[1]) {
- case '+':
- return round(a[0] + b[0], 4).toString() + a[1]
- case '*':
- return round(a[0] * b[0], 4).toString() + a[1]
- case '-':
- return round(a[0] - b[0], 4).toString() + a[1]
- case '/':
- return round(a[0] / b[0], 4).toString() + a[1]
+ // If we have a top-level `calc(…)` node then the evaluation did not resolve
+ // to a single value and we consider it to be incomplete
+ if (isFunctionNode(node)) {
+ if (node.name[1] === 'calc(') return null
}
- return null
+ return stringify(...node.tokens())
}
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
index 6eb840ef..3adbdb44 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
@@ -95,6 +95,8 @@ test('Evaluating CSS calc expressions', () => {
expect(replaceCssCalc('calc(1.25 / 0.875)', (node) => evaluateExpression(node.value))).toBe(
'1.4286',
)
+
+ expect(replaceCssCalc('calc(1/4 * 100%)', (node) => evaluateExpression(node.value))).toBe('25%')
})
test('Inlining calc expressions using the design system', () => {
diff --git a/packages/tailwindcss-language-service/tsconfig.json b/packages/tailwindcss-language-service/tsconfig.json
index 883356e7..46eb230c 100644
--- a/packages/tailwindcss-language-service/tsconfig.json
+++ b/packages/tailwindcss-language-service/tsconfig.json
@@ -1,7 +1,7 @@
{
"include": ["src", "../../types"],
"compilerOptions": {
- "module": "NodeNext",
+ "module": "ES2022",
"lib": ["ES2022"],
"target": "ES2022",
"importHelpers": true,
@@ -13,7 +13,7 @@
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
- "moduleResolution": "NodeNext",
+ "moduleResolution": "Bundler",
"skipLibCheck": true,
"jsx": "react",
"esModuleInterop": true
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2255d6ad..f0971dbb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -248,15 +248,18 @@ importers:
packages/tailwindcss-language-service:
dependencies:
+ '@csstools/css-calc':
+ specifier: 2.1.2
+ version: 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
'@csstools/css-parser-algorithms':
- specifier: 2.1.1
- version: 2.1.1(@csstools/css-tokenizer@2.1.1)
+ specifier: 3.0.4
+ version: 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer':
- specifier: 2.1.1
- version: 2.1.1
+ specifier: 3.0.3
+ version: 3.0.3
'@csstools/media-query-list-parser':
specifier: 2.0.4
- version: 2.0.4(@csstools/css-parser-algorithms@2.1.1(@csstools/css-tokenizer@2.1.1))(@csstools/css-tokenizer@2.1.1)
+ version: 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
'@types/culori':
specifier: ^2.1.0
version: 2.1.1
@@ -440,15 +443,22 @@ packages:
resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==}
engines: {node: '>=6.9.0'}
- '@csstools/css-parser-algorithms@2.1.1':
- resolution: {integrity: sha512-viRnRh02AgO4mwIQb2xQNJju0i+Fh9roNgmbR5xEuG7J3TGgxjnE95HnBLgsFJOJOksvcfxOUCgODcft6Y07cA==}
- engines: {node: ^14 || ^16 || >=18}
+ '@csstools/css-calc@2.1.2':
+ resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==}
+ engines: {node: '>=18'}
peerDependencies:
- '@csstools/css-tokenizer': ^2.1.1
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
- '@csstools/css-tokenizer@2.1.1':
- resolution: {integrity: sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==}
- engines: {node: ^14 || ^16 || >=18}
+ '@csstools/css-parser-algorithms@3.0.4':
+ resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-tokenizer@3.0.3':
+ resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
+ engines: {node: '>=18'}
'@csstools/media-query-list-parser@2.0.4':
resolution: {integrity: sha512-GyYot6jHgcSDZZ+tLSnrzkR7aJhF2ZW6d+CXH66mjy5WpAQhZD4HDke2OQ36SivGRWlZJpAz7TzbW6OKlEpxAA==}
@@ -800,13 +810,11 @@ packages:
'@parcel/watcher-darwin-arm64@2.5.1':
resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.1':
resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.1':
@@ -830,31 +838,26 @@ packages:
'@parcel/watcher-linux-arm64-glibc@2.5.1':
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-arm64-musl@2.5.1':
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-x64-glibc@2.5.1':
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [linux]
'@parcel/watcher-linux-x64-musl@2.5.1':
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [linux]
'@parcel/watcher-win32-arm64@2.5.1':
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.1':
@@ -866,7 +869,6 @@ packages:
'@parcel/watcher-win32-x64@2.5.1':
resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.1':
@@ -2908,16 +2910,21 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
- '@csstools/css-parser-algorithms@2.1.1(@csstools/css-tokenizer@2.1.1)':
+ '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
dependencies:
- '@csstools/css-tokenizer': 2.1.1
+ '@csstools/css-tokenizer': 3.0.3
- '@csstools/css-tokenizer@2.1.1': {}
+ '@csstools/css-tokenizer@3.0.3': {}
- '@csstools/media-query-list-parser@2.0.4(@csstools/css-parser-algorithms@2.1.1(@csstools/css-tokenizer@2.1.1))(@csstools/css-tokenizer@2.1.1)':
+ '@csstools/media-query-list-parser@2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
dependencies:
- '@csstools/css-parser-algorithms': 2.1.1(@csstools/css-tokenizer@2.1.1)
- '@csstools/css-tokenizer': 2.1.1
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
'@esbuild/aix-ppc64@0.21.5':
optional: true
From 5c825459e8dbacef374f0b6d6a1967223685dedb Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 23 Apr 2025 11:21:36 -0400
Subject: [PATCH 059/108] Guard against infinitely recursive theme key lookup
(#1332)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When constructing the design system we compute color swatches for all
classes. This process involves:
- Computing the CSS for a utility
- Inlining any values from the theme, recursively
- Scanning for CSS color syntax that we can compute a swatch for
We guard against directly self-recursive replacements because of how the
replacement itself functions. However, if you wrapped the variable in
something then we would effectively recurse into the expression and try
to replace the variable again. This caused the expansion to then be
wrapped and then try again resulting in an infinite loop.
This fixes it by keeping track of seen variables during a replacement
and if we've seen it _and_ the expansion contains a var(…) anywhere
we'll only replace it once. This logic is not perfect as the thing that
matters is that the replacement _itself_ results in a recursion but this
is good enough for now.
Fixes #1329
---
.../src/util/rewriting/index.test.ts | 46 +++++++++++++++++--
.../src/util/rewriting/var-fallbacks.ts | 13 +++++-
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
3 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
index 3adbdb44..de51a2b7 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts
@@ -79,9 +79,49 @@ test('replacing CSS variables with their fallbacks (when they have them)', () =>
expect(replaceCssVarsWithFallbacks(state, 'var(--level-3)')).toBe('blue')
// Circular replacements don't cause infinite loops
- expect(replaceCssVarsWithFallbacks(state, 'var(--circular-1)')).toBe('var(--circular-3)')
- expect(replaceCssVarsWithFallbacks(state, 'var(--circular-2)')).toBe('var(--circular-1)')
- expect(replaceCssVarsWithFallbacks(state, 'var(--circular-3)')).toBe('var(--circular-2)')
+ expect(replaceCssVarsWithFallbacks(state, 'var(--circular-1)')).toBe('var(--circular-1)')
+ expect(replaceCssVarsWithFallbacks(state, 'var(--circular-2)')).toBe('var(--circular-2)')
+ expect(replaceCssVarsWithFallbacks(state, 'var(--circular-3)')).toBe('var(--circular-3)')
+})
+
+test('recursive theme replacements', () => {
+ let map = new Map([
+ ['--color-a', 'var(--color-a)'],
+ ['--color-b', 'rgb(var(--color-b))'],
+ ['--color-c', 'rgb(var(--channel) var(--channel) var(--channel))'],
+ ['--channel', '255'],
+
+ ['--color-d', 'rgb(var(--indirect) var(--indirect) var(--indirect))'],
+ ['--indirect', 'var(--channel)'],
+ ['--channel', '255'],
+
+ ['--mutual-a', 'calc(var(--mutual-b) * 1)'],
+ ['--mutual-b', 'calc(var(--mutual-a) + 1)'],
+ ])
+
+ let state: State = {
+ enabled: true,
+ designSystem: {
+ theme: { prefix: null } as any,
+ resolveThemeValue: (name) => map.get(name) ?? null,
+ } as DesignSystem,
+ }
+
+ expect(replaceCssVarsWithFallbacks(state, 'var(--color-a)')).toBe('var(--color-a)')
+ expect(replaceCssVarsWithFallbacks(state, 'var(--color-b)')).toBe('rgb(var(--color-b))')
+ expect(replaceCssVarsWithFallbacks(state, 'var(--color-c)')).toBe('rgb(255 255 255)')
+
+ // This one is wrong but fixing it without breaking the infinite recursion guard is complex
+ expect(replaceCssVarsWithFallbacks(state, 'var(--color-d)')).toBe(
+ 'rgb(255 var(--indirect) var(--indirect))',
+ )
+
+ expect(replaceCssVarsWithFallbacks(state, 'var(--mutual-a)')).toBe(
+ 'calc(calc(var(--mutual-a) + 1) * 1)',
+ )
+ expect(replaceCssVarsWithFallbacks(state, 'var(--mutual-b)')).toBe(
+ 'calc(calc(var(--mutual-b) * 1) + 1)',
+ )
})
test('Evaluating CSS calc expressions', () => {
diff --git a/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts b/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
index 91dcc91d..2eb8c918 100644
--- a/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
+++ b/packages/tailwindcss-language-service/src/util/rewriting/var-fallbacks.ts
@@ -3,14 +3,25 @@ import { resolveVariableValue } from './lookup'
import { replaceCssVars } from './replacements'
export function replaceCssVarsWithFallbacks(state: State, str: string): string {
+ let seen = new Set()
+
return replaceCssVars(str, {
replace({ name, fallback }) {
// Replace with the value from the design system first. The design system
// take precedences over other sources as that emulates the behavior of a
// browser where the fallback is only used if the variable is defined.
if (state.designSystem && name.startsWith('--')) {
+ // TODO: This isn't quite right as we might skip expanding a variable
+ // that should be expanded
+ if (seen.has(name)) return null
let value = resolveVariableValue(state.designSystem, name)
- if (value !== null) return value
+ if (value !== null) {
+ if (value.includes('var(')) {
+ seen.add(name)
+ }
+
+ return value
+ }
}
if (fallback) {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 4162495c..a734adf3 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,7 @@
- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
- Support class function hovers in Svelte and HTML `` blocks ([#1311](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1311))
+- Guard against recursive theme key lookup ([#1332](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1332))
# 0.14.15
From 7afe2267750022b6acda8de300b0982ed458881a Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Wed, 23 Apr 2025 11:29:56 -0400
Subject: [PATCH 060/108] Update changelog
---
packages/vscode-tailwindcss/CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index a734adf3..8d59ad80 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -4,6 +4,7 @@
- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
- Support class function hovers in Svelte and HTML `` blocks ([#1311](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1311))
+- Evaluate complex `calc(…)` expressions in completions and equivalents ([#1316](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1316))
- Guard against recursive theme key lookup ([#1332](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1332))
# 0.14.15
From a3810154913309b9084d535e86553cf6e861521c Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 24 Apr 2025 13:23:12 -0400
Subject: [PATCH 061/108] 0.14.16
---
packages/tailwindcss-language-server/package.json | 2 +-
packages/tailwindcss-language-service/package.json | 2 +-
packages/vscode-tailwindcss/CHANGELOG.md | 4 ++++
packages/vscode-tailwindcss/package.json | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json
index 0813d13e..8d2617f9 100644
--- a/packages/tailwindcss-language-server/package.json
+++ b/packages/tailwindcss-language-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-server",
- "version": "0.14.15",
+ "version": "0.14.16",
"description": "Tailwind CSS Language Server",
"license": "MIT",
"repository": {
diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json
index 4a11e6c7..f7928b3e 100644
--- a/packages/tailwindcss-language-service/package.json
+++ b/packages/tailwindcss-language-service/package.json
@@ -1,6 +1,6 @@
{
"name": "@tailwindcss/language-service",
- "version": "0.14.15",
+ "version": "0.14.16",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 8d59ad80..122181c8 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,6 +2,10 @@
## Prerelease
+- Nothing yet!
+
+# 0.14.16
+
- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
- Support class function hovers in Svelte and HTML `` blocks ([#1311](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1311))
- Evaluate complex `calc(…)` expressions in completions and equivalents ([#1316](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1316))
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index d3d30911..079055b0 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -1,6 +1,6 @@
{
"name": "vscode-tailwindcss",
- "version": "0.14.15",
+ "version": "0.14.16",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"author": "Brad Cornes ",
From f511faa48fa21223f7edbc51635bd4d7b5a08d0f Mon Sep 17 00:00:00 2001
From: Akat <24731539+AkatQuas@users.noreply.github.com>
Date: Tue, 29 Apr 2025 22:44:41 +0800
Subject: [PATCH 062/108] Launch extension with additional options when
debugging (#1339)
This PR add several options for starting extension in VS Code.
Co-authored-by: TaotaoDong
---
.vscode/launch.json | 10 +++++++++-
.vscode/tasks.json | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/.vscode/launch.json b/.vscode/launch.json
index fd173bdc..04478652 100755
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -7,7 +7,15 @@
"request": "launch",
"name": "Launch Client",
"runtimeExecutable": "${execPath}",
- "args": ["--extensionDevelopmentPath=${workspaceRoot}/packages/vscode-tailwindcss"],
+ "args": [
+ // enable this flag if you want to activate the extension only when you are debugging the extension
+ // "--disable-extensions",
+ "--disable-updates",
+ "--disable-workspace-trust",
+ "--skip-release-notes",
+ "--skip-welcome",
+ "--extensionDevelopmentPath=${workspaceRoot}/packages/vscode-tailwindcss"
+ ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/packages/vscode-tailwindcss/dist/**/*.js"],
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 1abde264..aec43080 100755
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -23,6 +23,9 @@
"panel": "dedicated",
"reveal": "never"
},
+ "options": {
+ "cwd": "${workspaceFolder}/packages/vscode-tailwindcss"
+ },
"problemMatcher": ["$tsc-watch"]
}
]
From f432c77c5567246056db1e8ba9c28aaae1bd80ae Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Mon, 5 May 2025 10:04:45 -0400
Subject: [PATCH 063/108] Rework capability registration and handler setup
(#1327)
This PR changes how capability registration works such that:
- Dynamic registrations are honored on a per-capability basis rather
than only when the client supports several capabilities being
dynamically registered
- Most capabilities are registered _once_ after project discovery and
only re-registered after a server restart
- The completion capability will not re-register itself if trigger
characters have not changed
This should, in most cases, mean that *all* supported capabilities are
dynamically registered once at startup. Trigger characters only change
for projects where the variant separator is customizable, has been
customized, and includes a character that is not already considered a
trigger character (v4 does *not* allow customizing the variant separator
but v3 did)
Fixes #1319
- [x] Need to look into creating a reproduction w/ 500-ish v3 config
files in it to see if I can repro the original issue. We call
`updateCapabilities` after a build finishes and its likely that this
is/was slow enough with IntelliSense trying to handle 500 separate v3
projects at once
---
.../src/projects.ts | 5 +
.../tailwindcss-language-server/src/tw.ts | 203 +++++++++++-------
.../tests/env/capabilities.test.ts | 184 ++++++++++++++++
.../tests/env/restart.test.ts | 18 ++
.../tests/utils/client.ts | 79 +++++++
packages/vscode-tailwindcss/CHANGELOG.md | 2 +-
6 files changed, 410 insertions(+), 81 deletions(-)
create mode 100644 packages/tailwindcss-language-server/tests/env/capabilities.test.ts
diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index 04160569..21e7bb3b 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -1086,6 +1086,11 @@ export async function createProjectService(
refreshDiagnostics()
updateCapabilities()
+
+ let isTestMode = params.initializationOptions?.testMode ?? false
+ if (!isTestMode) return
+
+ connection.sendNotification('@/tailwindCSS/projectReloaded')
}
for (let entry of projectConfig.config.entries) {
diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts
index 20ac0158..792c887d 100644
--- a/packages/tailwindcss-language-server/src/tw.ts
+++ b/packages/tailwindcss-language-server/src/tw.ts
@@ -21,6 +21,8 @@ import type {
WorkspaceFolder,
CodeLensParams,
CodeLens,
+ ServerCapabilities,
+ ClientCapabilities,
} from 'vscode-languageserver/node'
import {
CompletionRequest,
@@ -624,6 +626,8 @@ export class TW {
console.log(`[Global] Initializing projects...`)
+ await this.updateCommonCapabilities()
+
// init projects for documents that are _already_ open
let readyDocuments: string[] = []
let enabledProjectCount = 0
@@ -640,8 +644,6 @@ export class TW {
console.log(`[Global] Initialized ${enabledProjectCount} projects`)
- this.setupLSPHandlers()
-
this.disposables.push(
this.connection.onDidChangeConfiguration(async ({ settings }) => {
let previousExclude = globalSettings.tailwindCSS.files.exclude
@@ -763,7 +765,7 @@ export class TW {
this.connection,
params,
this.documentService,
- () => this.updateCapabilities(),
+ () => this.updateProjectCapabilities(),
() => {
for (let document of this.documentService.getAllDocuments()) {
let project = this.getProject(document)
@@ -810,9 +812,7 @@ export class TW {
}
setupLSPHandlers() {
- if (this.lspHandlersAdded) {
- return
- }
+ if (this.lspHandlersAdded) return
this.lspHandlersAdded = true
this.connection.onHover(this.onHover.bind(this))
@@ -858,43 +858,84 @@ export class TW {
}
}
- private updateCapabilities() {
- if (!supportsDynamicRegistration(this.initializeParams)) {
- this.connection.client.register(DidChangeConfigurationNotification.type, undefined)
- return
+ // Common capabilities are always supported by the language server and do not
+ // require any project-specific information to know how to configure them.
+ //
+ // These capabilities will stay valid until/unless the server has to restart
+ // in which case they'll be unregistered and then re-registered once project
+ // discovery has completed
+ private commonRegistrations: BulkUnregistration | undefined
+ private async updateCommonCapabilities() {
+ let capabilities = BulkRegistration.create()
+
+ let client = this.initializeParams.capabilities
+
+ if (client.textDocument?.hover?.dynamicRegistration) {
+ capabilities.add(HoverRequest.type, { documentSelector: null })
}
- if (this.registrations) {
- this.registrations.then((r) => r.dispose())
+ if (client.textDocument?.colorProvider?.dynamicRegistration) {
+ capabilities.add(DocumentColorRequest.type, { documentSelector: null })
}
- let projects = Array.from(this.projects.values())
+ if (client.textDocument?.codeAction?.dynamicRegistration) {
+ capabilities.add(CodeActionRequest.type, { documentSelector: null })
+ }
- let capabilities = BulkRegistration.create()
+ if (client.textDocument?.codeLens?.dynamicRegistration) {
+ capabilities.add(CodeLensRequest.type, { documentSelector: null })
+ }
+
+ if (client.textDocument?.documentLink?.dynamicRegistration) {
+ capabilities.add(DocumentLinkRequest.type, { documentSelector: null })
+ }
+
+ if (client.workspace?.didChangeConfiguration?.dynamicRegistration) {
+ capabilities.add(DidChangeConfigurationNotification.type, undefined)
+ }
- // TODO: We should *not* be re-registering these capabilities
- // IDEA: These should probably be static registrations up front
- capabilities.add(HoverRequest.type, { documentSelector: null })
- capabilities.add(DocumentColorRequest.type, { documentSelector: null })
- capabilities.add(CodeActionRequest.type, { documentSelector: null })
- capabilities.add(CodeLensRequest.type, { documentSelector: null })
- capabilities.add(DocumentLinkRequest.type, { documentSelector: null })
- capabilities.add(DidChangeConfigurationNotification.type, undefined)
-
- // TODO: Only re-register this if trigger characters change
- capabilities.add(CompletionRequest.type, {
+ this.commonRegistrations = await this.connection.client.register(capabilities)
+ }
+
+ // These capabilities depend on the projects we've found to appropriately
+ // configure them. This may mean collecting information from all discovered
+ // projects to determine what we can do and how
+ private updateProjectCapabilities() {
+ this.updateTriggerCharacters()
+ }
+
+ private lastTriggerCharacters: Set | undefined
+ private completionRegistration: Disposable | undefined
+ private async updateTriggerCharacters() {
+ // If the client does not suppory dynamic registration of completions then
+ // we cannot update the set of trigger characters
+ let client = this.initializeParams.capabilities
+ if (!client.textDocument?.completion?.dynamicRegistration) return
+
+ // The new set of trigger characters is all the static ones plus
+ // any characters from any separator in v3 config
+ let chars = new Set(TRIGGER_CHARACTERS)
+
+ for (let project of this.projects.values()) {
+ let sep = project.state.separator
+ if (typeof sep !== 'string') continue
+
+ sep = sep.slice(-1)
+ if (!sep) continue
+
+ chars.add(sep)
+ }
+
+ // If the trigger characters haven't changed then we don't need to do anything
+ if (equal(Array.from(chars), Array.from(this.lastTriggerCharacters ?? []))) return
+ this.lastTriggerCharacters = chars
+
+ this.completionRegistration?.dispose()
+ this.completionRegistration = await this.connection.client.register(CompletionRequest.type, {
documentSelector: null,
resolveProvider: true,
- triggerCharacters: [
- ...TRIGGER_CHARACTERS,
- ...projects
- .map((project) => project.state.separator)
- .filter((sep) => typeof sep === 'string')
- .map((sep) => sep.slice(-1)),
- ].filter(Boolean),
+ triggerCharacters: Array.from(chars),
})
-
- this.registrations = this.connection.client.register(capabilities)
}
private getProject(document: TextDocumentIdentifier): ProjectService {
@@ -1016,47 +1057,58 @@ export class TW {
this.connection.onInitialize(async (params: InitializeParams): Promise => {
this.initializeParams = params
- if (supportsDynamicRegistration(params)) {
- return {
- capabilities: {
- textDocumentSync: TextDocumentSyncKind.Full,
- workspace: {
- workspaceFolders: {
- changeNotifications: true,
- },
- },
- },
- }
- }
-
this.setupLSPHandlers()
return {
- capabilities: {
- textDocumentSync: TextDocumentSyncKind.Full,
- hoverProvider: true,
- colorProvider: true,
- codeActionProvider: true,
- codeLensProvider: {
- resolveProvider: false,
- },
- documentLinkProvider: {},
- completionProvider: {
- resolveProvider: true,
- triggerCharacters: [...TRIGGER_CHARACTERS, ':'],
- },
- workspace: {
- workspaceFolders: {
- changeNotifications: true,
- },
- },
- },
+ capabilities: this.computeServerCapabilities(params.capabilities),
}
})
this.connection.onInitialized(() => this.init())
}
+ computeServerCapabilities(client: ClientCapabilities) {
+ let capabilities: ServerCapabilities = {
+ textDocumentSync: TextDocumentSyncKind.Full,
+ workspace: {
+ workspaceFolders: {
+ changeNotifications: true,
+ },
+ },
+ }
+
+ if (!client.textDocument?.hover?.dynamicRegistration) {
+ capabilities.hoverProvider = true
+ }
+
+ if (!client.textDocument?.colorProvider?.dynamicRegistration) {
+ capabilities.colorProvider = true
+ }
+
+ if (!client.textDocument?.codeAction?.dynamicRegistration) {
+ capabilities.codeActionProvider = true
+ }
+
+ if (!client.textDocument?.codeLens?.dynamicRegistration) {
+ capabilities.codeLensProvider = {
+ resolveProvider: false,
+ }
+ }
+
+ if (!client.textDocument?.completion?.dynamicRegistration) {
+ capabilities.completionProvider = {
+ resolveProvider: true,
+ triggerCharacters: [...TRIGGER_CHARACTERS, ':'],
+ }
+ }
+
+ if (!client.textDocument?.documentLink?.dynamicRegistration) {
+ capabilities.documentLinkProvider = {}
+ }
+
+ return capabilities
+ }
+
listen() {
this.connection.listen()
}
@@ -1070,10 +1122,11 @@ export class TW {
this.refreshDiagnostics()
- if (this.registrations) {
- this.registrations.then((r) => r.dispose())
- this.registrations = undefined
- }
+ this.commonRegistrations?.dispose()
+ this.commonRegistrations = undefined
+
+ this.completionRegistration?.dispose()
+ this.completionRegistration = undefined
this.disposables.forEach((d) => d.dispose())
this.disposables.length = 0
@@ -1106,13 +1159,3 @@ export class TW {
}
}
}
-
-function supportsDynamicRegistration(params: InitializeParams): boolean {
- return (
- params.capabilities.textDocument?.hover?.dynamicRegistration &&
- params.capabilities.textDocument?.colorProvider?.dynamicRegistration &&
- params.capabilities.textDocument?.codeAction?.dynamicRegistration &&
- params.capabilities.textDocument?.completion?.dynamicRegistration &&
- params.capabilities.textDocument?.documentLink?.dynamicRegistration
- )
-}
diff --git a/packages/tailwindcss-language-server/tests/env/capabilities.test.ts b/packages/tailwindcss-language-server/tests/env/capabilities.test.ts
new file mode 100644
index 00000000..22eac608
--- /dev/null
+++ b/packages/tailwindcss-language-server/tests/env/capabilities.test.ts
@@ -0,0 +1,184 @@
+import { expect } from 'vitest'
+import { defineTest, js } from '../../src/testing'
+import { createClient } from '../utils/client'
+import * as fs from 'node:fs/promises'
+
+defineTest({
+ name: 'Changing the separator registers new trigger characters',
+ fs: {
+ 'tailwind.config.js': js`
+ module.exports = {
+ separator: ':',
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ root, client }) => {
+ // Initially don't have any registered capabilities because dynamic
+ // registration is delayed until after project initialization
+ expect(client.serverCapabilities).toEqual([])
+
+ // We open a document so a project gets initialized
+ await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ // And now capabilities are registered
+ expect(client.serverCapabilities).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ method: 'textDocument/hover',
+ }),
+
+ expect.objectContaining({
+ method: 'textDocument/completion',
+ registerOptions: {
+ documentSelector: null,
+ resolveProvider: true,
+ triggerCharacters: ['"', "'", '`', ' ', '.', '(', '[', ']', '!', '/', '-', ':'],
+ },
+ }),
+ ]),
+ )
+
+ let countBeforeChange = client.serverCapabilities.length
+ let capabilitiesDidChange = Promise.race([
+ new Promise
((_, reject) => {
+ setTimeout(() => reject('capabilities did not change within 5s'), 5_000)
+ }),
+
+ new Promise((resolve) => {
+ client.onServerCapabilitiesChanged(() => {
+ if (client.serverCapabilities.length !== countBeforeChange) return
+ resolve()
+ })
+ }),
+ ])
+
+ await fs.writeFile(
+ `${root}/tailwind.config.js`,
+ js`
+ module.exports = {
+ separator: '_',
+ }
+ `,
+ )
+
+ // After changing the config
+ client.notifyChangedFiles({
+ changed: [`${root}/tailwind.config.js`],
+ })
+
+ // We should see that the capabilities have changed
+ await capabilitiesDidChange
+
+ // Capabilities are now registered
+ expect(client.serverCapabilities).toContainEqual(
+ expect.objectContaining({
+ method: 'textDocument/hover',
+ }),
+ )
+
+ expect(client.serverCapabilities).toContainEqual(
+ expect.objectContaining({
+ method: 'textDocument/completion',
+ registerOptions: {
+ documentSelector: null,
+ resolveProvider: true,
+ triggerCharacters: ['"', "'", '`', ' ', '.', '(', '[', ']', '!', '/', '-', '_'],
+ },
+ }),
+ )
+
+ expect(client.serverCapabilities).not.toContainEqual(
+ expect.objectContaining({
+ method: 'textDocument/completion',
+ registerOptions: {
+ documentSelector: null,
+ resolveProvider: true,
+ triggerCharacters: ['"', "'", '`', ' ', '.', '(', '[', ']', '!', '/', '-', ':'],
+ },
+ }),
+ )
+ },
+})
+
+defineTest({
+ name: 'Config updates do not register new trigger characters if the separator has not changed',
+ fs: {
+ 'tailwind.config.js': js`
+ module.exports = {
+ separator: ':',
+ theme: {
+ colors: {
+ primary: '#f00',
+ }
+ }
+ }
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ root, client }) => {
+ // Initially don't have any registered capabilities because dynamic
+ // registration is delayed until after project initialization
+ expect(client.serverCapabilities).toEqual([])
+
+ // We open a document so a project gets initialized
+ await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ // And now capabilities are registered
+ expect(client.serverCapabilities).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ method: 'textDocument/hover',
+ }),
+
+ expect.objectContaining({
+ method: 'textDocument/completion',
+ registerOptions: {
+ documentSelector: null,
+ resolveProvider: true,
+ triggerCharacters: ['"', "'", '`', ' ', '.', '(', '[', ']', '!', '/', '-', ':'],
+ },
+ }),
+ ]),
+ )
+
+ let idsBefore = client.serverCapabilities.map((cap) => cap.id)
+
+ await fs.writeFile(
+ `${root}/tailwind.config.js`,
+ js`
+ module.exports = {
+ separator: ':',
+ theme: {
+ colors: {
+ primary: '#0f0',
+ }
+ }
+ }
+ `,
+ )
+
+ let didReload = new Promise((resolve) => {
+ client.conn.onNotification('@/tailwindCSS/projectReloaded', resolve)
+ })
+
+ // After changing the config
+ client.notifyChangedFiles({
+ changed: [`${root}/tailwind.config.js`],
+ })
+
+ // Wait for the project to finish building
+ await didReload
+
+ // No capabilities should have changed
+ let idsAfter = client.serverCapabilities.map((cap) => cap.id)
+
+ expect(idsBefore).toEqual(idsAfter)
+ },
+})
diff --git a/packages/tailwindcss-language-server/tests/env/restart.test.ts b/packages/tailwindcss-language-server/tests/env/restart.test.ts
index 49b632c3..35b595f8 100644
--- a/packages/tailwindcss-language-server/tests/env/restart.test.ts
+++ b/packages/tailwindcss-language-server/tests/env/restart.test.ts
@@ -140,6 +140,9 @@ defineTest({
},
})
+ expect(client.serverCapabilities).not.toEqual([])
+ let ids1 = client.serverCapabilities.map((cap) => cap.id)
+
// Remove the CSS file
let didRestart = new Promise((resolve) => {
client.conn.onNotification('@/tailwindCSS/serverRestarted', resolve)
@@ -147,6 +150,9 @@ defineTest({
await fs.unlink(path.resolve(root, 'app.css'))
await didRestart
+ expect(client.serverCapabilities).not.toEqual([])
+ let ids2 = client.serverCapabilities.map((cap) => cap.id)
+
//
// ^
let hover2 = await doc.hover({ line: 0, character: 13 })
@@ -164,11 +170,23 @@ defineTest({
)
await didRestartAgain
+ expect(client.serverCapabilities).not.toEqual([])
+ let ids3 = client.serverCapabilities.map((cap) => cap.id)
+
await new Promise((resolve) => setTimeout(resolve, 500))
//
// ^
let hover3 = await doc.hover({ line: 0, character: 13 })
expect(hover3).toEqual(null)
+
+ expect(ids1).not.toContainEqual(expect.toBeOneOf(ids2))
+ expect(ids1).not.toContainEqual(expect.toBeOneOf(ids3))
+
+ expect(ids2).not.toContainEqual(expect.toBeOneOf(ids1))
+ expect(ids2).not.toContainEqual(expect.toBeOneOf(ids3))
+
+ expect(ids3).not.toContainEqual(expect.toBeOneOf(ids1))
+ expect(ids3).not.toContainEqual(expect.toBeOneOf(ids2))
},
})
diff --git a/packages/tailwindcss-language-server/tests/utils/client.ts b/packages/tailwindcss-language-server/tests/utils/client.ts
index 760b805b..681843a8 100644
--- a/packages/tailwindcss-language-server/tests/utils/client.ts
+++ b/packages/tailwindcss-language-server/tests/utils/client.ts
@@ -6,16 +6,21 @@ import {
CompletionList,
CompletionParams,
Diagnostic,
+ DidChangeWatchedFilesNotification,
Disposable,
DocumentLink,
DocumentLinkRequest,
DocumentSymbol,
DocumentSymbolRequest,
+ FileChangeType,
+ FileEvent,
Hover,
NotificationHandler,
ProtocolConnection,
PublishDiagnosticsParams,
+ Registration,
SymbolInformation,
+ UnregistrationRequest,
WorkspaceFolder,
} from 'vscode-languageserver'
import type { Position } from 'vscode-languageserver-textdocument'
@@ -83,6 +88,12 @@ export interface DocumentDescriptor {
settings?: Settings
}
+export interface ChangedFiles {
+ created?: string[]
+ changed?: string[]
+ deleted?: string[]
+}
+
export interface ClientDocument {
/**
* The URI to the document
@@ -191,6 +202,21 @@ export interface Client extends ClientWorkspace {
*/
readonly conn: ProtocolConnection
+ /**
+ * Get the currently registered server capabilities
+ */
+ serverCapabilities: Registration[]
+
+ /**
+ * Get the currently registered server capabilities
+ */
+ onServerCapabilitiesChanged(cb: () => void): void
+
+ /**
+ * Tell the server that files on disk have changed
+ */
+ notifyChangedFiles(changes: ChangedFiles): Promise
+
/**
* Get a workspace by name
*/
@@ -428,12 +454,40 @@ export async function createClient(opts: ClientOptions): Promise {
})
}
+ let serverCapabilityChangeCallbacks: (() => void)[] = []
+
+ function onServerCapabilitiesChanged(cb: () => void) {
+ serverCapabilityChangeCallbacks.push(cb)
+ }
+
+ let registeredCapabilities: Registration[] = []
+
conn.onRequest(RegistrationRequest.type, ({ registrations }) => {
trace('Registering capabilities')
for (let registration of registrations) {
+ registeredCapabilities.push(registration)
+ trace('-', registration.method)
+ }
+
+ for (let cb of serverCapabilityChangeCallbacks) cb()
+ })
+
+ conn.onRequest(UnregistrationRequest.type, ({ unregisterations }) => {
+ trace('Unregistering capabilities')
+
+ let idsToRemove = new Set()
+
+ for (let registration of unregisterations) {
+ idsToRemove.add(registration.id)
trace('-', registration.method)
}
+
+ registeredCapabilities = registeredCapabilities.filter(
+ (capability) => !idsToRemove.has(capability.id),
+ )
+
+ for (let cb of serverCapabilityChangeCallbacks) cb()
})
// TODO: Remove this its a hack
@@ -493,8 +547,33 @@ export async function createClient(opts: ClientOptions): Promise {
await initPromise
}
+ function notifyChangedFiles(changes: ChangedFiles) {
+ let events: FileEvent[] = []
+
+ for (const path of changes?.created ?? []) {
+ events.push({ uri: URI.file(path).toString(), type: FileChangeType.Created })
+ }
+
+ for (const path of changes?.changed ?? []) {
+ events.push({ uri: URI.file(path).toString(), type: FileChangeType.Changed })
+ }
+
+ for (const path of changes?.deleted ?? []) {
+ events.push({ uri: URI.file(path).toString(), type: FileChangeType.Deleted })
+ }
+
+ return conn.sendNotification(DidChangeWatchedFilesNotification.type, {
+ changes: events,
+ })
+ }
+
return {
...clientWorkspaces[0],
+ get serverCapabilities() {
+ return registeredCapabilities
+ },
+ onServerCapabilitiesChanged,
+ notifyChangedFiles,
workspace,
updateSettings,
}
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 122181c8..53c9592a 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Improve dynamic capability registration in the language server ([#1327](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1327))
# 0.14.16
From 03ad23dc955ec87447d7593782ea438057057639 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 8 May 2025 11:04:43 -0400
Subject: [PATCH 064/108] Update default file exclusions (#1336)
IntelliSense, by default, ignores files for:
- Version control (`.git`, `.hg`, `.svn`)
- NPM (`node_modules`)
I'm adding three more groups to this:
- Python virtual environment folders (`.venv`, `venv`)
- Yarn v2+ metadata & caches (`.yarn`)
- *some* build caches (`.next`, `.turbo`, `.parcel-cache`,
`__pycache__`)
Discovered that we should update these when triaging #1312 (that one was
specifically about python virtual envs)
---
.../tests/env/ignored.test.ts | 111 ++++++++++++++++++
.../src/util/state.ts | 35 +++++-
packages/vscode-tailwindcss/CHANGELOG.md | 3 +
3 files changed, 148 insertions(+), 1 deletion(-)
create mode 100644 packages/tailwindcss-language-server/tests/env/ignored.test.ts
diff --git a/packages/tailwindcss-language-server/tests/env/ignored.test.ts b/packages/tailwindcss-language-server/tests/env/ignored.test.ts
new file mode 100644
index 00000000..f5f7d01f
--- /dev/null
+++ b/packages/tailwindcss-language-server/tests/env/ignored.test.ts
@@ -0,0 +1,111 @@
+import { expect } from 'vitest'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
+import dedent from 'dedent'
+
+let ignored = css`
+ @import 'tailwindcss';
+ @theme {
+ --color-primary: #c0ffee;
+ }
+`
+
+let found = css`
+ @import 'tailwindcss';
+ @theme {
+ --color-primary: rebeccapurple;
+ }
+`
+
+defineTest({
+ name: 'various build folders and caches are ignored by default',
+ fs: {
+ // All of these should be ignored
+ 'aaa/.git/app.css': ignored,
+ 'aaa/.hg/app.css': ignored,
+ 'aaa/.svn/app.css': ignored,
+ 'aaa/node_modules/app.css': ignored,
+ 'aaa/.yarn/app.css': ignored,
+ 'aaa/.venv/app.css': ignored,
+ 'aaa/venv/app.css': ignored,
+ 'aaa/.next/app.css': ignored,
+ 'aaa/.parcel-cache/app.css': ignored,
+ 'aaa/.svelte-kit/app.css': ignored,
+ 'aaa/.turbo/app.css': ignored,
+ 'aaa/__pycache__/app.css': ignored,
+
+ // But this one should not be
+ 'zzz/app.css': found,
+ },
+
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '',
+ })
+
+ //
+ // ^
+ let hover = await doc.hover({ line: 0, character: 13 })
+ expect(hover).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .bg-primary {
+ background-color: var(--color-primary) /* rebeccapurple = #663399 */;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 22 },
+ },
+ })
+ },
+})
+
+defineTest({
+ name: 'ignores can be overridden',
+ fs: {
+ 'aaa/app.css': ignored,
+ 'bbb/.git/app.css': found,
+ },
+
+ prepare: async ({ root }) => ({
+ client: await createClient({
+ root,
+ settings: {
+ tailwindCSS: {
+ files: {
+ exclude: ['**/aaa/**'],
+ },
+ },
+ },
+ }),
+ }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ //
+ // ^
+ let hover = await doc.hover({ line: 0, character: 13 })
+ expect(hover).toEqual({
+ contents: {
+ language: 'css',
+ value: dedent`
+ .bg-primary {
+ background-color: var(--color-primary) /* rebeccapurple = #663399 */;
+ }
+ `,
+ },
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 22 },
+ },
+ })
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 26cc2ed0..ecd9d0d8 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -208,7 +208,40 @@ export function getDefaultTailwindSettings(): Settings {
},
showPixelEquivalents: true,
includeLanguages: {},
- files: { exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'] },
+ files: {
+ exclude: [
+ // These paths need to be universally ignorable. This means that we
+ // should only consider hidden folders with a commonly understood
+ // meaning unless there is a very good reason to do otherwise.
+ //
+ // This means that things like `build`, `target`, `cache`, etc… are
+ // not appropriate to include even though _in many cases_ they might
+ // be ignorable. The names are too general and ignoring them could
+ // cause us to ignore actual project files.
+
+ // Version Control
+ '**/.git/**',
+ '**/.hg/**',
+ '**/.svn/**',
+
+ // NPM
+ '**/node_modules/**',
+
+ // Yarn v2+ metadata & caches
+ '**/.yarn/**',
+
+ // Python Virtual Environments
+ '**/.venv/**',
+ '**/venv/**',
+
+ // Build caches
+ '**/.next/**',
+ '**/.parcel-cache/**',
+ '**/.svelte-kit/**',
+ '**/.turbo/**',
+ '**/__pycache__/**',
+ ],
+ },
experimental: {
classRegex: [],
configFile: null,
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 53c9592a..e57ef565 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -3,6 +3,9 @@
## Prerelease
- Improve dynamic capability registration in the language server ([#1327](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1327))
+- Ignore Python virtual env directories by default ([#1336](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1336))
+- Ignore Yarn v2+ metadata & cache directories by default ([#1336](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1336))
+- Ignore some build caches by default ([#1336](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1336))
# 0.14.16
From dfce548a0f20811d7c40b1b1ec2e07d43a99f470 Mon Sep 17 00:00:00 2001
From: Jordan Pittman
Date: Thu, 8 May 2025 14:21:46 -0400
Subject: [PATCH 065/108] Enable isolated declarations for
`@tailwindcss/language-service` (#1348)
Just some code cleanup (also a bugfix revealed by doing this)
---
.../src/testing/index.ts | 12 +--
.../provideInvalidApplyCodeActions.ts | 2 +-
.../src/completionProvider.ts | 2 +-
.../diagnostics/getCssConflictDiagnostics.ts | 2 +-
.../src/metadata/extensions.ts | 4 +-
.../src/util/absoluteRange.ts | 2 +-
.../src/util/braceLevel.ts | 4 +-
.../src/util/colorEquivalents.ts | 84 +++++++++--------
.../src/util/css.ts | 4 +-
.../src/util/cssObjToAst.ts | 4 +-
.../src/util/estimated-class-size.ts | 2 +-
.../src/util/flagEnabled.ts | 2 +-
.../src/util/format-bytes.ts | 2 +-
.../src/util/getLanguageBoundaries.ts | 2 +-
.../src/util/jit.ts | 2 +-
.../src/util/language-blocks.ts | 2 +-
.../src/util/languages.ts | 17 ++--
.../src/util/lexers.ts | 6 +-
.../src/util/pixelEquivalents.ts | 92 +++++++++----------
.../src/util/rewriting/add-theme-values.ts | 2 +-
.../src/util/rewriting/inline-theme-values.ts | 2 +-
.../src/util/rewriting/lookup.ts | 2 +-
.../src/util/segment.ts | 2 +-
.../src/util/splice-changes-into-string.ts | 2 +-
.../src/util/test-utils.ts | 20 ++--
.../src/util/v4/ast.ts | 2 +-
.../tsconfig.json | 3 +-
27 files changed, 146 insertions(+), 136 deletions(-)
diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts
index 0a11bb9d..efce9fd2 100644
--- a/packages/tailwindcss-language-server/src/testing/index.ts
+++ b/packages/tailwindcss-language-server/src/testing/index.ts
@@ -3,7 +3,7 @@ import * as os from 'node:os'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as proc from 'node:child_process'
-import dedent from 'dedent'
+import dedent, { type Dedent } from 'dedent'
export interface TestUtils> {
/** The "cwd" for this test */
@@ -160,8 +160,8 @@ async function installDependenciesIn(dir: string) {
})
}
-export const css = dedent
-export const scss = dedent
-export const html = dedent
-export const js = dedent
-export const json = dedent
+export const css: Dedent = dedent
+export const scss: Dedent = dedent
+export const html: Dedent = dedent
+export const js: Dedent = dedent
+export const json: Dedent = dedent
diff --git a/packages/tailwindcss-language-service/src/codeActions/provideInvalidApplyCodeActions.ts b/packages/tailwindcss-language-service/src/codeActions/provideInvalidApplyCodeActions.ts
index dcbda0e0..0ead7b36 100644
--- a/packages/tailwindcss-language-service/src/codeActions/provideInvalidApplyCodeActions.ts
+++ b/packages/tailwindcss-language-service/src/codeActions/provideInvalidApplyCodeActions.ts
@@ -212,7 +212,7 @@ function classNameToAst(
obj = rule
}
- return cssObjToAst(obj, state.modules.postcss)
+ return cssObjToAst(obj, state.modules.postcss.module)
}
function appendPseudosToSelector(selector: string, pseudos: string[]): string | null {
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 843e9a8e..bf9685a9 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -2230,7 +2230,7 @@ export async function doComplete(
document: TextDocument,
position: Position,
context?: CompletionContext,
-) {
+): Promise {
if (state === null) return { items: [], isIncomplete: false }
const result =
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
index ec6dc166..dcf61089 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
@@ -239,7 +239,7 @@ export function visit(
nodes: postcss.AnyNode[],
cb: (node: postcss.AnyNode, path: postcss.AnyNode[]) => void,
path: postcss.AnyNode[] = [],
-) {
+): void {
for (let child of nodes) {
path = [...path, child]
cb(child, path)
diff --git a/packages/tailwindcss-language-service/src/metadata/extensions.ts b/packages/tailwindcss-language-service/src/metadata/extensions.ts
index 15babe94..34726d8f 100644
--- a/packages/tailwindcss-language-service/src/metadata/extensions.ts
+++ b/packages/tailwindcss-language-service/src/metadata/extensions.ts
@@ -72,5 +72,5 @@ let templateExtensions = [
'rs',
]
-export const IS_SCRIPT_SOURCE = new RegExp(`\\.(${scriptExtensions.join('|')})$`)
-export const IS_TEMPLATE_SOURCE = new RegExp(`\\.(${templateExtensions.join('|')})$`)
+export const IS_SCRIPT_SOURCE: RegExp = new RegExp(`\\.(${scriptExtensions.join('|')})$`)
+export const IS_TEMPLATE_SOURCE: RegExp = new RegExp(`\\.(${templateExtensions.join('|')})$`)
diff --git a/packages/tailwindcss-language-service/src/util/absoluteRange.ts b/packages/tailwindcss-language-service/src/util/absoluteRange.ts
index 643de4ed..de6cb642 100644
--- a/packages/tailwindcss-language-service/src/util/absoluteRange.ts
+++ b/packages/tailwindcss-language-service/src/util/absoluteRange.ts
@@ -1,6 +1,6 @@
import type { Range } from 'vscode-languageserver'
-export function absoluteRange(range: Range, reference?: Range) {
+export function absoluteRange(range: Range, reference?: Range): Range {
return {
start: {
line: (reference?.start.line || 0) + range.start.line,
diff --git a/packages/tailwindcss-language-service/src/util/braceLevel.ts b/packages/tailwindcss-language-service/src/util/braceLevel.ts
index a7245b17..46d2f5cd 100644
--- a/packages/tailwindcss-language-service/src/util/braceLevel.ts
+++ b/packages/tailwindcss-language-service/src/util/braceLevel.ts
@@ -1,4 +1,4 @@
-export function braceLevel(text: string) {
+export function braceLevel(text: string): number {
let count = 0
for (let i = text.length - 1; i >= 0; i--) {
@@ -10,7 +10,7 @@ export function braceLevel(text: string) {
return count
}
-export function parenLevel(text: string) {
+export function parenLevel(text: string): number {
let count = 0
for (let i = text.length - 1; i >= 0; i--) {
diff --git a/packages/tailwindcss-language-service/src/util/colorEquivalents.ts b/packages/tailwindcss-language-service/src/util/colorEquivalents.ts
index 95ab47bc..a33f9070 100644
--- a/packages/tailwindcss-language-service/src/util/colorEquivalents.ts
+++ b/packages/tailwindcss-language-service/src/util/colorEquivalents.ts
@@ -1,4 +1,4 @@
-import type { Plugin } from 'postcss'
+import type { Plugin, PluginCreator } from 'postcss'
import parseValue from 'postcss-value-parser'
import { inGamut } from 'culori'
import { formatColor, getColorFromValue } from './color'
@@ -16,51 +16,55 @@ export function getEquivalentColor(value: string): string {
return formatColor(color)
}
-export function equivalentColorValues({ comments }: { comments: Comment[] }): Plugin {
- return {
- postcssPlugin: 'plugin',
- Declaration(decl) {
- if (!allowedFunctions.some((fn) => decl.value.includes(fn))) {
- return
- }
-
- parseValue(decl.value).walk((node) => {
- if (node.type !== 'function') {
- return true
+export const equivalentColorValues: PluginCreator