diff --git a/CHANGELOG.md b/CHANGELOG.md index b9fcc4bbba73..fb255fd29e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add first draft of new wide-gamut color palette ([#14693](https://github.com/tailwindlabs/tailwindcss/pull/14693)) - _Upgrade (experimental)_: Migrate `theme(…)` calls to `var(…)` or to the modern `theme(…)` syntax ([#14664](https://github.com/tailwindlabs/tailwindcss/pull/14664), [#14695](https://github.com/tailwindlabs/tailwindcss/pull/14695)) +- _Upgrade (experimental)_: Migrate `plugins` with options to CSS ([#14700](https://github.com/tailwindlabs/tailwindcss/pull/14700)) ### Fixed diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index 06904b2a486c..6478e6e92219 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -106,7 +106,7 @@ test( --font-family-sans: Inter, system-ui, sans-serif; --font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; --radius-4xl: 2rem; @@ -155,14 +155,33 @@ test( import customPlugin from './custom-plugin' export default { - plugins: [typography, customPlugin], + plugins: [ + typography, + customPlugin({ + 'is-null': null, + 'is-true': true, + 'is-false': false, + 'is-int': 1234567, + 'is-float': 1.35, + 'is-sci': 1.35e-5, + 'is-str-null': 'null', + 'is-str-true': 'true', + 'is-str-false': 'false', + 'is-str-int': '1234567', + 'is-str-float': '1.35', + 'is-str-sci': '1.35e-5', + 'is-arr': ['foo', 'bar'], + 'is-arr-mixed': [null, true, false, 1234567, 1.35, 'foo', 'bar', 'true'], + }), + ], } satisfies Config `, 'custom-plugin.js': ts` - export default function ({ addVariant }) { + import plugin from 'tailwindcss/plugin' + export default plugin.withOptions((_options) => ({ addVariant }) => { addVariant('inverted', '@media (inverted-colors: inverted)') addVariant('hocus', ['&:focus', '&:hover']) - } + }) `, 'src/input.css': css` @tailwind base; @@ -180,7 +199,22 @@ test( @import 'tailwindcss'; @plugin '@tailwindcss/typography'; - @plugin '../custom-plugin'; + @plugin '../custom-plugin' { + is-null: null; + is-true: true; + is-false: false; + is-int: 1234567; + is-float: 1.35; + is-sci: 0.0000135; + is-str-null: 'null'; + is-str-true: 'true'; + is-str-false: 'false'; + is-str-int: '1234567'; + is-str-float: '1.35'; + is-str-sci: '1.35e-5'; + is-arr: 'foo', 'bar'; + is-arr-mixed: null, true, false, 1234567, 1.35, 'foo', 'bar', 'true'; + } " `) diff --git a/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts b/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts index e90e0e2637ca..b545d6009e72 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts @@ -18,7 +18,15 @@ export function formatNodes(): Plugin { // Format the nodes await Promise.all( nodesToFormat.map(async (node) => { - node.replaceWith(parse(await format(node.toString(), { parser: 'css', semi: true }))) + node.replaceWith( + parse( + await format(node.toString(), { + parser: 'css', + semi: true, + singleQuote: true, + }), + ), + ) }), ) } diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts index cbf96d58f041..d8134cf48421 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts @@ -413,7 +413,7 @@ it('should migrate classes with attribute selectors', async () => { `), ).toMatchInlineSnapshot(` "@utility no-scrollbar { - &[data-checked=""] { + &[data-checked=''] { display: none; } }" diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts index 158432d6b92f..ccb99296e6cf 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts @@ -63,7 +63,22 @@ export function migrateConfig( plugin.path[0] === '.' ? relativeToStylesheet(sheet, path.resolve(plugin.base, plugin.path)) : plugin.path - css += `@plugin '${relative}';\n` + + if (plugin.options === null) { + css += `@plugin '${relative}';\n` + } else { + css += `@plugin '${relative}' {\n` + for (let [property, value] of Object.entries(plugin.options)) { + css += ` ${property}: ${ + Array.isArray(value) + ? value.map((v) => (typeof v === 'string' ? quoteString(v) : '' + v)).join(', ') + : typeof value === 'string' + ? quoteString(value) + : '' + value + };\n` + } + css += '}\n' + } } if (jsConfigMigration.plugins.length > 0) { css = css + '\n' @@ -149,3 +164,7 @@ function relativeToStylesheet(sheet: Stylesheet, absolute: string) { // glob. return normalizePath(relative) } + +function quoteString(value: string): string { + return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'` +} diff --git a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts index 57a4d6188e15..b000619eda2e 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts +++ b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts @@ -24,7 +24,7 @@ export type JSConfigMigration = // Could not convert the config file, need to inject it as-is in a @config directive null | { sources: { base: string; pattern: string }[] - plugins: { base: string; path: string }[] + plugins: { base: string; path: string; options: null | StaticPluginOptions }[] css: string }