This PR bumps the repo to pnpm v11 (from v9). I kept running into weird Windows specific issues for the integration tests due to some shims but they all pass right now. Bumping to pnpm v11 also meant that everything is driven by a `pnpm-workspace.yaml` file, and changes applied via the `pnpm` field in the `package.json` don't work anymore. This also moved the `node-linker` setup that was defined in the `.npmrc` file for the wasm oxide build into the `pnpm-workspace.yaml` file. Ideally this is scoped to just this package, but I couldn't get that to work, so it's applied to all packages right now. [ci-all]
2187 lines
57 KiB
TypeScript
2187 lines
57 KiB
TypeScript
import path from 'node:path'
|
|
import { describe } from 'vitest'
|
|
import { css, html, json, test, ts, txt } from '../utils'
|
|
|
|
test(
|
|
`upgrade JS config files with flat theme values, darkMode, and content fields`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}', './node_modules/my-external-lib/**/*.{html}'],
|
|
theme: {
|
|
boxShadow: {
|
|
sm: '0 2px 6px rgb(15 23 42 / 0.08)',
|
|
},
|
|
colors: {
|
|
red: {
|
|
400: '#f87171',
|
|
500: 'red',
|
|
},
|
|
superRed: '#ff0000',
|
|
steel: 'rgb(70 130 180 / <alpha-value>)',
|
|
smoke: 'rgba(245, 245, 245, var(--smoke-alpha, <alpha-value>))',
|
|
},
|
|
ringColor: {
|
|
DEFAULT: '#c0ffee',
|
|
},
|
|
opacity: {
|
|
superOpaque: '0.95',
|
|
},
|
|
fontSize: {
|
|
xs: ['0.75rem', { lineHeight: '1rem' }],
|
|
sm: ['0.875rem', { lineHeight: '1.5rem' }],
|
|
base: ['1rem', { lineHeight: '2rem' }],
|
|
lg: ['1.125rem', '2.5rem'],
|
|
xl: ['1.5rem', '3rem', 'invalid'],
|
|
'2xl': ['2rem'],
|
|
},
|
|
width: {
|
|
px: '1px',
|
|
auto: 'auto',
|
|
1: '0.25rem',
|
|
1.5: '0.375rem',
|
|
2: '0.5rem',
|
|
2.5: '0.625rem',
|
|
3: '0.75rem',
|
|
3.5: '0.875rem',
|
|
4: '1rem',
|
|
5: '1.25rem',
|
|
6: '1.5rem',
|
|
8: '2rem',
|
|
10: '2.5rem',
|
|
11: '2.75rem',
|
|
12: '3rem',
|
|
16: '4rem',
|
|
24: '6rem',
|
|
32: '8rem',
|
|
40: '10rem',
|
|
48: '12rem',
|
|
64: '16rem',
|
|
80: '20rem',
|
|
96: '24rem',
|
|
128: '32rem',
|
|
|
|
full: '100%',
|
|
0: '0%',
|
|
'1/2': '50%',
|
|
'1/3': 'calc(100% / 3)',
|
|
'2/3': 'calc(100% / 3 * 2)',
|
|
'1/4': '25%',
|
|
'3/4': '75%',
|
|
'1/5': '20%',
|
|
'2/5': '40%',
|
|
'3/5': '60%',
|
|
'4/5': '80%',
|
|
'1/6': 'calc(100% / 6)',
|
|
'5/6': 'calc(100% / 6 * 5)',
|
|
'1/7': 'calc(100% / 7)',
|
|
'1/10': 'calc(100% / 10)',
|
|
'3/10': 'calc(100% / 10 * 3)',
|
|
'7/10': 'calc(100% / 10 * 7)',
|
|
'9/10': 'calc(100% / 10 * 9)',
|
|
screen: '100vw',
|
|
|
|
'full-minus-80': 'calc(100% - 20rem)',
|
|
'full-minus-96': 'calc(100% - 24rem)',
|
|
|
|
'225px': '225px',
|
|
},
|
|
extend: {
|
|
colors: {
|
|
red: {
|
|
500: '#ef4444',
|
|
600: '#dc2626',
|
|
},
|
|
},
|
|
fontFamily: {
|
|
sans: 'Inter, system-ui, sans-serif',
|
|
display: ['Cabinet Grotesk', ...defaultTheme.fontFamily.sans],
|
|
},
|
|
borderRadius: {
|
|
'4xl': '2rem',
|
|
},
|
|
keyframes: {
|
|
'spin-clockwise': {
|
|
'0%': { transform: 'rotate(0deg)' },
|
|
'100%': { transform: 'rotate(360deg)' },
|
|
},
|
|
'spin-counterclockwise': {
|
|
'0%': { transform: 'rotate(0deg)' },
|
|
'100%': { transform: 'rotate(-360deg)' },
|
|
},
|
|
},
|
|
animation: {
|
|
'spin-clockwise': 'spin-clockwise 1s linear infinite',
|
|
'spin-counterclockwise': 'spin-counterclockwise 1s linear infinite',
|
|
},
|
|
letterSpacing: {
|
|
superWide: '0.25em',
|
|
},
|
|
lineHeight: {
|
|
superLoose: '3',
|
|
},
|
|
},
|
|
},
|
|
plugins: [],
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
// prettier-ignore
|
|
'src/test.js': ts`
|
|
export default {
|
|
'shouldNotMigrate': !border.test + '',
|
|
'filter': 'drop-shadow(0 0 0.5rem #000)',
|
|
}
|
|
`,
|
|
'src/index.html': html`
|
|
<div
|
|
class="[letter-spacing:theme(letterSpacing.superWide)] [line-height:theme(lineHeight.superLoose)]"
|
|
></div>
|
|
<div class="text-superRed/superOpaque leading-superLoose"></div>
|
|
`,
|
|
'node_modules/my-external-lib/src/template.html': html`
|
|
<div class="text-red-500">
|
|
Hello world!
|
|
</div>
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.{css,js,html}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/index.html ---
|
|
<div
|
|
class="tracking-superWide leading-superLoose"
|
|
></div>
|
|
<div class="text-superRed/superOpaque leading-superLoose"></div>
|
|
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@source '../node_modules/my-external-lib/**/*.{html}';
|
|
|
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
|
|
@theme {
|
|
--shadow-*: initial;
|
|
--shadow-sm: 0 2px 6px rgb(15 23 42 / 0.08);
|
|
|
|
--color-*: initial;
|
|
--color-red-400: #f87171;
|
|
--color-red-500: #ef4444;
|
|
--color-red-600: #dc2626;
|
|
|
|
--color-superRed: #ff0000;
|
|
--color-steel: rgb(70 130 180);
|
|
--color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1));
|
|
|
|
--default-ring-color: #c0ffee;
|
|
|
|
--opacity-*: initial;
|
|
--opacity-superOpaque: 95%;
|
|
|
|
--text-*: initial;
|
|
--text-xs: 0.75rem;
|
|
--text-xs--line-height: 1rem;
|
|
--text-sm: 0.875rem;
|
|
--text-sm--line-height: 1.5rem;
|
|
--text-base: 1rem;
|
|
--text-base--line-height: 2rem;
|
|
--text-lg: 1.125rem;
|
|
--text-lg--line-height: 2.5rem;
|
|
--text-xl: 1.5rem;
|
|
--text-xl--line-height: 3rem;
|
|
--text-2xl: 2rem;
|
|
|
|
--width-*: initial;
|
|
--width-0: 0%;
|
|
--width-1: 0.25rem;
|
|
--width-2: 0.5rem;
|
|
--width-3: 0.75rem;
|
|
--width-4: 1rem;
|
|
--width-5: 1.25rem;
|
|
--width-6: 1.5rem;
|
|
--width-8: 2rem;
|
|
--width-10: 2.5rem;
|
|
--width-11: 2.75rem;
|
|
--width-12: 3rem;
|
|
--width-16: 4rem;
|
|
--width-24: 6rem;
|
|
--width-32: 8rem;
|
|
--width-40: 10rem;
|
|
--width-48: 12rem;
|
|
--width-64: 16rem;
|
|
--width-80: 20rem;
|
|
--width-96: 24rem;
|
|
--width-128: 32rem;
|
|
--width-px: 1px;
|
|
--width-auto: auto;
|
|
--width-1_5: 0.375rem;
|
|
--width-2_5: 0.625rem;
|
|
--width-3_5: 0.875rem;
|
|
--width-full: 100%;
|
|
--width-1\\/2: 50%;
|
|
--width-1\\/3: calc(100% / 3);
|
|
--width-2\\/3: calc(100% / 3 * 2);
|
|
--width-1\\/4: 25%;
|
|
--width-3\\/4: 75%;
|
|
--width-1\\/5: 20%;
|
|
--width-2\\/5: 40%;
|
|
--width-3\\/5: 60%;
|
|
--width-4\\/5: 80%;
|
|
--width-1\\/6: calc(100% / 6);
|
|
--width-5\\/6: calc(100% / 6 * 5);
|
|
--width-1\\/7: calc(100% / 7);
|
|
--width-1\\/10: calc(100% / 10);
|
|
--width-3\\/10: calc(100% / 10 * 3);
|
|
--width-7\\/10: calc(100% / 10 * 7);
|
|
--width-9\\/10: calc(100% / 10 * 9);
|
|
--width-screen: 100vw;
|
|
--width-full-minus-80: calc(100% - 20rem);
|
|
--width-full-minus-96: calc(100% - 24rem);
|
|
--width-225px: 225px;
|
|
|
|
--font-sans: Inter, system-ui, sans-serif;
|
|
--font-display:
|
|
Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
|
|
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
|
|
--radius-4xl: 2rem;
|
|
|
|
--animate-spin-clockwise: spin-clockwise 1s linear infinite;
|
|
--animate-spin-counterclockwise: spin-counterclockwise 1s linear infinite;
|
|
|
|
--tracking-superWide: 0.25em;
|
|
|
|
--leading-superLoose: 3;
|
|
|
|
@keyframes spin-clockwise {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
@keyframes spin-counterclockwise {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(-360deg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
--- src/test.js ---
|
|
export default {
|
|
'shouldNotMigrate': !border.test + '',
|
|
'filter': 'drop-shadow(0 0 0.5rem #000)',
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toBe('')
|
|
},
|
|
)
|
|
|
|
test(
|
|
'skips gitignored template files even when they are explicitly referenced in `content`',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'.gitignore': txt`
|
|
node_modules
|
|
src/ignored
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
module.exports = {
|
|
content: [
|
|
'./src/migrate-me.html',
|
|
'./src/ignored/do-not-migrate-me.html',
|
|
'./node_modules/my-external-lib/template.html',
|
|
],
|
|
theme: {},
|
|
plugins: [],
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
'src/migrate-me.html': html`
|
|
<div class="order-[0]"></div>
|
|
`,
|
|
'src/ignored/do-not-migrate-me.html': html`
|
|
<div class="order-[0]"></div>
|
|
`,
|
|
'node_modules/my-external-lib/template.html': html`
|
|
<div
|
|
class="order-[0]"
|
|
></div>
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(
|
|
await fs.dumpFiles(
|
|
'{src/**/*.{css,html},node_modules/my-external-lib/template.html,.gitignore}',
|
|
),
|
|
).toMatchInlineSnapshot(`
|
|
"
|
|
--- .gitignore ---
|
|
node_modules
|
|
src/ignored
|
|
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@source './ignored/do-not-migrate-me.html';
|
|
@source '../node_modules/my-external-lib/template.html';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
--- src/migrate-me.html ---
|
|
<div class="order-0"></div>
|
|
|
|
--- node_modules/my-external-lib/template.html ---
|
|
<div
|
|
class="order-[0]"
|
|
></div>
|
|
|
|
--- src/ignored/do-not-migrate-me.html ---
|
|
<div class="order-[0]"></div>
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'upgrades JS config files with plugins',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/typography": "^0.5.15",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
import typography from '@tailwindcss/typography'
|
|
import customPlugin from './custom-plugin'
|
|
|
|
export default {
|
|
darkMode: [
|
|
'variant',
|
|
[
|
|
'@media not print { .dark & }',
|
|
'@media not eink { .dark & }',
|
|
'&:where(.dark, .dark *)',
|
|
],
|
|
],
|
|
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`
|
|
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;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@plugin '@tailwindcss/typography';
|
|
@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';
|
|
}
|
|
|
|
@custom-variant dark {
|
|
@media not print {
|
|
.dark & {
|
|
@slot;
|
|
}
|
|
}
|
|
@media not eink {
|
|
.dark & {
|
|
@slot;
|
|
}
|
|
}
|
|
&:where(.dark, .dark *) {
|
|
@slot;
|
|
}
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect(await fs.dumpFiles('tailwind.config.ts')).toMatchInlineSnapshot(`
|
|
"
|
|
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'upgrades JS config files with functions in the theme config',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
extend: {
|
|
colors: ({ colors }) => ({
|
|
gray: colors.neutral,
|
|
}),
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.{css,ts}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@theme {
|
|
--color-gray-50: oklch(98.5% 0 0);
|
|
--color-gray-100: oklch(97% 0 0);
|
|
--color-gray-200: oklch(92.2% 0 0);
|
|
--color-gray-300: oklch(87% 0 0);
|
|
--color-gray-400: oklch(70.8% 0 0);
|
|
--color-gray-500: oklch(55.6% 0 0);
|
|
--color-gray-600: oklch(43.9% 0 0);
|
|
--color-gray-700: oklch(37.1% 0 0);
|
|
--color-gray-800: oklch(26.9% 0 0);
|
|
--color-gray-900: oklch(20.5% 0 0);
|
|
--color-gray-950: oklch(14.5% 0 0);
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect(await fs.dumpFiles('tailwind.config.ts')).toMatchInlineSnapshot(`
|
|
"
|
|
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'does not upgrade JS config files with theme keys contributed to by plugins in the theme config',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
typography: {
|
|
DEFAULT: {
|
|
css: {
|
|
'--tw-prose-body': 'red',
|
|
color: 'var(--tw-prose-body)',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
@config '../tailwind.config.ts';
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@config '../tailwind.config.ts';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect(await fs.dumpFiles('tailwind.config.ts')).toMatchInlineSnapshot(`
|
|
"
|
|
--- tailwind.config.ts ---
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
typography: {
|
|
DEFAULT: {
|
|
css: {
|
|
'--tw-prose-body': 'red',
|
|
color: 'var(--tw-prose-body)',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} satisfies Config
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
`does not upgrade JS config files with inline plugins`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
plugins: [function complexConfig() {}],
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@config '../tailwind.config.ts';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect(await fs.dumpFiles('tailwind.config.ts')).toMatchInlineSnapshot(`
|
|
"
|
|
--- tailwind.config.ts ---
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
plugins: [function complexConfig() {}],
|
|
} satisfies Config
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
`does not upgrade JS config files with non-simple screens object`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
screens: {
|
|
xl: { min: '1024px', max: '1279px' },
|
|
tall: { raw: '(min-height: 800px)' },
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@config '../tailwind.config.ts';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect(await fs.dumpFiles('tailwind.config.ts')).toMatchInlineSnapshot(`
|
|
"
|
|
--- tailwind.config.ts ---
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
screens: {
|
|
xl: { min: '1024px', max: '1279px' },
|
|
tall: { raw: '(min-height: 800px)' },
|
|
},
|
|
},
|
|
} satisfies Config
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'multi-root project',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
|
|
// Project A
|
|
'project-a/tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: 'red',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'project-a/src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
@config "../tailwind.config.ts";
|
|
`,
|
|
'project-a/src/index.html': html`<div class="!text-primary"></div>`,
|
|
|
|
// Project B
|
|
'project-b/tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: 'blue',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'project-b/src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
@config "../tailwind.config.ts";
|
|
`,
|
|
'project-b/src/index.html': html`<div class="!text-primary"></div>`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('project-{a,b}/**/*.{css,ts}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- project-a/src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@theme {
|
|
--color-primary: red;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
--- project-b/src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@theme {
|
|
--color-primary: blue;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate sources when pointing to folders outside the project root',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
|
|
'frontend/tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html', '../backend/mails/**/*.blade.php'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: 'red',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'frontend/src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
@config "../tailwind.config.ts";
|
|
`,
|
|
'frontend/src/index.html': html`<div class="!text-primary"></div>`,
|
|
|
|
'backend/mails/welcome.blade.php': html`<div class="!text-primary"></div>`,
|
|
},
|
|
},
|
|
async ({ root, exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade', {
|
|
cwd: path.join(root, 'frontend'),
|
|
})
|
|
|
|
expect(await fs.dumpFiles('frontend/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- frontend/src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@source '../../backend/mails/**/*.blade.php';
|
|
|
|
@theme {
|
|
--color-primary: red;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate aria theme keys to custom variants',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
aria: {
|
|
// Built-in (not really, but visible because of intellisense)
|
|
busy: 'busy="true"',
|
|
|
|
// Automatically handled by bare values
|
|
foo: 'foo="true"',
|
|
|
|
// Quotes are optional in CSS for these kinds of attribute
|
|
// selectors
|
|
bar: 'bar=true',
|
|
|
|
// Not automatically handled by bare values because names differ
|
|
baz: 'qux="true"',
|
|
|
|
// Completely custom
|
|
asc: 'sort="ascending"',
|
|
desc: 'sort="descending"',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@custom-variant aria-baz (&[aria-qux="true"]);
|
|
@custom-variant aria-asc (&[aria-sort="ascending"]);
|
|
@custom-variant aria-desc (&[aria-sort="descending"]);
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate data theme keys to custom variants',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
data: {
|
|
// Automatically handled by bare values
|
|
foo: 'foo',
|
|
|
|
// Not automatically handled by bare values because names differ
|
|
bar: 'baz',
|
|
|
|
// Custom
|
|
checked: 'ui~="checked"',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@custom-variant data-bar (&[data-baz]);
|
|
@custom-variant data-checked (&[data-ui~="checked"]);
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate supports theme keys to custom variants',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
export default {
|
|
content: {
|
|
relative: true,
|
|
files: ['./src/**/*.html'],
|
|
},
|
|
theme: {
|
|
extend: {
|
|
supports: {
|
|
// Automatically handled by bare values (using CSS variable as the value)
|
|
foo: 'foo: var(--foo)', // parentheses are optional
|
|
bar: '(bar: var(--bar))',
|
|
|
|
// Not automatically handled by bare values because names differ
|
|
foo: 'bar: var(--foo)', // parentheses are optional
|
|
bar: '(qux: var(--bar))',
|
|
|
|
// Custom
|
|
grid: 'display: grid',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@custom-variant supports-foo {
|
|
@supports (bar: var(--foo)) {
|
|
@slot;
|
|
}
|
|
}
|
|
@custom-variant supports-bar {
|
|
@supports ((qux: var(--bar))) {
|
|
@slot;
|
|
}
|
|
}
|
|
@custom-variant supports-grid {
|
|
@supports (display: grid) {
|
|
@slot;
|
|
}
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
describe('border compatibility', () => {
|
|
test(
|
|
'migrate border compatibility',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
// Empty / default config
|
|
export default {
|
|
theme: {
|
|
extend: {},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate border compatibility if a custom border color is used',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
extend: {
|
|
borderColor: ({ colors }) => ({
|
|
DEFAULT: colors.blue[500],
|
|
}),
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: oklch(62.3% 0.214 259.815);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate border compatibility if a custom border color is used, that matches the default v4 border color',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {
|
|
extend: {
|
|
borderColor: ({ colors }) => ({
|
|
DEFAULT: 'currentcolor',
|
|
}),
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate border compatibility in the file that uses the `@import "tailwindcss"` import',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`@import './tailwind.css';`,
|
|
'src/tailwind.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import './tailwind.css';
|
|
|
|
--- src/tailwind.css ---
|
|
@import 'tailwindcss';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrate border compatibility in the file that uses the `@import "tailwindcss/preflight"` import',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
theme: {},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@import './base.css';
|
|
@import './my-base.css';
|
|
@import './utilities.css';
|
|
`,
|
|
'src/base.css': css`@tailwind base;`,
|
|
'src/utilities.css': css`
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
'src/my-base.css': css`
|
|
@layer base {
|
|
html {
|
|
color: black;
|
|
}
|
|
}
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/base.css ---
|
|
@import 'tailwindcss/theme' layer(theme);
|
|
@import 'tailwindcss/preflight' layer(base);
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
--- src/input.css ---
|
|
@import './base.css';
|
|
@import './my-base.css';
|
|
@import './utilities.css';
|
|
|
|
--- src/my-base.css ---
|
|
@layer base {
|
|
html {
|
|
color: black;
|
|
}
|
|
}
|
|
|
|
--- src/utilities.css ---
|
|
@import 'tailwindcss/utilities' layer(utilities);
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrates extended spacing keys',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
content: ['./src/**/*.html'],
|
|
theme: {
|
|
extend: {
|
|
spacing: {
|
|
2: '0.5rem',
|
|
4.5: '1.125rem',
|
|
5.5: '1.375em', // Units are different from --spacing scale
|
|
13: '3.25rem',
|
|
100: '100px',
|
|
miami: '1337px',
|
|
},
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
.container {
|
|
width: theme(spacing.2);
|
|
width: theme(spacing[4.5]);
|
|
width: theme(spacing[5.5]);
|
|
width: theme(spacing[13]);
|
|
width: theme(spacing[100]);
|
|
width: theme(spacing.miami);
|
|
}
|
|
`,
|
|
'src/index.html': html`
|
|
<div
|
|
class="[width:theme(spacing.2)]
|
|
[width:theme(spacing[4.5])]
|
|
[width:theme(spacing[5.5])]
|
|
[width:theme(spacing[13])]
|
|
[width:theme(spacing[100])]
|
|
[width:theme(spacing.miami)]"
|
|
></div>
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/index.html ---
|
|
<div
|
|
class="w-2
|
|
w-4.5
|
|
w-5.5
|
|
w-13
|
|
w-100
|
|
w-miami"
|
|
></div>
|
|
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@theme {
|
|
--spacing-100: 100px;
|
|
--spacing-5_5: 1.375em;
|
|
--spacing-miami: 1337px;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
.container {
|
|
width: --spacing(2);
|
|
width: --spacing(4.5);
|
|
width: var(--spacing-5_5);
|
|
width: --spacing(13);
|
|
width: var(--spacing-100);
|
|
width: var(--spacing-miami);
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'retains overwriting spacing scale',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
content: ['./src/**/*.html'],
|
|
theme: {
|
|
spacing: {
|
|
2: '0.5rem',
|
|
4.5: '1.125rem',
|
|
5.5: '1.375em',
|
|
13: '3.25rem',
|
|
100: '100px',
|
|
miami: '1337px',
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
.container {
|
|
width: theme(spacing.2);
|
|
width: theme(spacing[4.5]);
|
|
width: theme(spacing[5.5]);
|
|
width: theme(spacing[13]);
|
|
width: theme(spacing[100]);
|
|
width: theme(spacing.miami);
|
|
}
|
|
`,
|
|
'src/index.html': html`
|
|
<div
|
|
class="[width:theme(spacing.2)]
|
|
[width:theme(spacing[4.5])]
|
|
[width:theme(spacing[5.5])]
|
|
[width:theme(spacing[13])]
|
|
[width:theme(spacing[100])]
|
|
[width:theme(spacing.miami)]"
|
|
></div>
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/index.html ---
|
|
<div
|
|
class="w-2
|
|
w-4.5
|
|
w-5.5
|
|
w-13
|
|
w-100
|
|
w-miami"
|
|
></div>
|
|
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@theme {
|
|
--spacing-*: initial;
|
|
--spacing-2: 0.5rem;
|
|
--spacing-13: 3.25rem;
|
|
--spacing-100: 100px;
|
|
--spacing-4_5: 1.125rem;
|
|
--spacing-5_5: 1.375em;
|
|
--spacing-miami: 1337px;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
|
|
.container {
|
|
width: var(--spacing-2);
|
|
width: var(--spacing-4_5);
|
|
width: var(--spacing-5_5);
|
|
width: var(--spacing-13);
|
|
width: var(--spacing-100);
|
|
width: var(--spacing-miami);
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
'migrates `container` component configurations',
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
content: ['./src/**/*.html'],
|
|
theme: {
|
|
container: {
|
|
center: true,
|
|
padding: {
|
|
DEFAULT: '2rem',
|
|
'2xl': '4rem',
|
|
},
|
|
screens: {
|
|
md: '48rem', // Matches a default --breakpoint
|
|
xl: '1280px',
|
|
'2xl': '1536px',
|
|
},
|
|
},
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
'src/index.html': html`
|
|
<div class="container"></div>
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/index.html ---
|
|
<div class="container"></div>
|
|
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@utility container {
|
|
margin-inline: auto;
|
|
padding-inline: 2rem;
|
|
@media (width >= --theme(--breakpoint-sm)) {
|
|
max-width: none;
|
|
}
|
|
@media (width >= 48rem) {
|
|
max-width: 48rem;
|
|
}
|
|
@media (width >= 1280px) {
|
|
max-width: 1280px;
|
|
}
|
|
@media (width >= 1536px) {
|
|
max-width: 1536px;
|
|
padding-inline: 4rem;
|
|
}
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
},
|
|
)
|
|
})
|
|
|
|
test(
|
|
`future and experimental keys are supported`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}'],
|
|
future: {
|
|
hoverOnlyWhenSupported: true,
|
|
respectDefaultRingColorOpacity: true,
|
|
disableColorOpacityUtilitiesByDefault: true,
|
|
relativeContentPathsByDefault: true,
|
|
},
|
|
experimental: {
|
|
generalizedModifiers: true,
|
|
},
|
|
theme: {
|
|
colors: {
|
|
red: {
|
|
400: '#f87171',
|
|
500: 'red',
|
|
},
|
|
},
|
|
},
|
|
plugins: [],
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
|
|
@theme {
|
|
--color-*: initial;
|
|
--color-red-400: #f87171;
|
|
--color-red-500: red;
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toBe('')
|
|
},
|
|
)
|
|
|
|
test(
|
|
`unknown future keys dont migrate the config`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}'],
|
|
future: {
|
|
something: true,
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@config '../tailwind.config.ts';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toMatchInlineSnapshot(`
|
|
"--- tailwind.config.ts ---
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}'],
|
|
future: {
|
|
something: true,
|
|
},
|
|
} satisfies Config"
|
|
`)
|
|
},
|
|
)
|
|
|
|
test(
|
|
`unknown experimental keys dont migrate the config`,
|
|
{
|
|
fs: {
|
|
'package.json': json`
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^3",
|
|
"@tailwindcss/upgrade": "workspace:^"
|
|
}
|
|
}
|
|
`,
|
|
'tailwind.config.ts': ts`
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}'],
|
|
experimental: {
|
|
something: true,
|
|
},
|
|
} satisfies Config
|
|
`,
|
|
'src/input.css': css`
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
`,
|
|
},
|
|
},
|
|
async ({ exec, fs, expect }) => {
|
|
await exec('pnpm exec upgrade')
|
|
|
|
expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(`
|
|
"
|
|
--- src/input.css ---
|
|
@import 'tailwindcss';
|
|
|
|
@config '../tailwind.config.ts';
|
|
|
|
/*
|
|
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentcolor);
|
|
}
|
|
}
|
|
"
|
|
`)
|
|
|
|
expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toMatchInlineSnapshot(`
|
|
"--- tailwind.config.ts ---
|
|
import { type Config } from 'tailwindcss'
|
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
|
|
module.exports = {
|
|
darkMode: 'selector',
|
|
content: ['./src/**/*.{html,js}'],
|
|
experimental: {
|
|
something: true,
|
|
},
|
|
} satisfies Config"
|
|
`)
|
|
},
|
|
)
|