mirror of
https://github.com/withastro/astro.git
synced 2025-03-31 23:31:30 -05:00
feat(tailwind): support v4 (#13049)
Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com> Co-authored-by: Sarah Rainsberger <5098874+sarah11918@users.noreply.github.com>
This commit is contained in:
parent
db252e0692
commit
2ed4bd90f2
40 changed files with 547 additions and 265 deletions
9
.changeset/curvy-penguins-act.md
Normal file
9
.changeset/curvy-penguins-act.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
'@astrojs/tailwind': major
|
||||
---
|
||||
|
||||
Deprecates the integration
|
||||
|
||||
Tailwind CSS now offers a Vite plugin which is the preferred way to use Tailwind 4 in Astro. Please uninstall `@astrojs/tailwind` and follow the [Tailwind documentation for manual installation](https://tailwindcss.com/docs/installation/framework-guides/astro).
|
||||
|
||||
This updated major version is only provided as a convenience for existing projects until they are able to migrate to the new plugin. It offers no additional functionality and is no longer recommended, but may continue to be used in your projects until it is removed entirely.
|
5
.changeset/four-chairs-exercise.md
Normal file
5
.changeset/four-chairs-exercise.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Updates `astro add tailwind` to add the `@tailwindcss/vite` plugin instead of the `@astrojs/tailwind` integration
|
|
@ -1,8 +1,10 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [tailwind()],
|
||||
vite: {
|
||||
plugins: [tailwindcss()]
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,12 +11,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.0.7",
|
||||
"@astrojs/tailwind": "^5.1.5",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@types/canvas-confetti": "^1.9.0",
|
||||
"astro": "^5.1.10",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
const { content } = Astro.props;
|
||||
---
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
// Component Imports
|
||||
import Button from '../components/Button.astro';
|
||||
|
||||
|
|
1
examples/with-tailwindcss/src/styles/global.css
Normal file
1
examples/with-tailwindcss/src/styles/global.css
Normal file
|
@ -0,0 +1 @@
|
|||
@import "tailwindcss";
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
|
@ -1,19 +1,13 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
|
||||
applyBaseStyles: false
|
||||
}),
|
||||
],
|
||||
devToolbar: {
|
||||
enabled: false,
|
||||
},
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/tailwind": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"astro": "workspace:*",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
import Button from '../components/Button.astro';
|
||||
import Complex from '../components/Complex.astro';
|
||||
// Component Imports
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
--color-dawn: #f3e9fa;
|
||||
--color-dusk: #514375;
|
||||
--color-midnight: #31274a;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
const path = require('node:path');
|
||||
|
||||
module.exports = {
|
||||
content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
dawn: '#f3e9fa',
|
||||
dusk: '#514375',
|
||||
midnight: '#31274a',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -43,7 +43,7 @@ test.describe('Tailwind CSS', () => {
|
|||
await expect(button, 'should have bg-purple-600').toHaveClass(/bg-purple-600/);
|
||||
await expect(button, 'should have background color').toHaveCSS(
|
||||
'background-color',
|
||||
'rgb(147, 51, 234)',
|
||||
'oklch(0.558 0.288 302.321)',
|
||||
);
|
||||
|
||||
await expect(button, 'should have lg:py-3').toHaveClass(/lg:py-3/);
|
||||
|
@ -66,7 +66,7 @@ test.describe('Tailwind CSS', () => {
|
|||
await expect(button, 'should have bg-purple-400').toHaveClass(/bg-purple-400/);
|
||||
await expect(button, 'should have background color').toHaveCSS(
|
||||
'background-color',
|
||||
'rgb(192, 132, 252)',
|
||||
'oklch(0.714 0.203 305.504)',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,14 +51,7 @@ const ALIASES = new Map([
|
|||
|
||||
const STUBS = {
|
||||
ASTRO_CONFIG: `import { defineConfig } from 'astro/config';\n// https://astro.build/config\nexport default defineConfig({});`,
|
||||
TAILWIND_CONFIG: `/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}\n`,
|
||||
TAILWIND_GLOBAL_CSS: `@import "tailwindcss";`,
|
||||
SVELTE_CONFIG: `\
|
||||
import { vitePreprocess } from '@astrojs/svelte';
|
||||
|
||||
|
@ -154,23 +147,28 @@ export async function add(names: string[], { flags }: AddOptions) {
|
|||
switch (installResult) {
|
||||
case UpdateResult.updated: {
|
||||
if (integrations.find((integration) => integration.id === 'tailwind')) {
|
||||
await setupIntegrationConfig({
|
||||
root,
|
||||
logger,
|
||||
const dir = new URL('./styles/', new URL(userConfig.srcDir ?? './src/', root));
|
||||
const styles = new URL('./global.css', dir);
|
||||
if (!existsSync(styles)) {
|
||||
logger.info(
|
||||
'SKIP_FORMAT',
|
||||
`\n ${magenta(`Astro will scaffold ${green('./src/styles/global.css')}.`)}\n`,
|
||||
);
|
||||
|
||||
flags,
|
||||
integrationName: 'Tailwind',
|
||||
possibleConfigFiles: [
|
||||
'./tailwind.config.cjs',
|
||||
'./tailwind.config.mjs',
|
||||
'./tailwind.config.ts',
|
||||
'./tailwind.config.mts',
|
||||
'./tailwind.config.cts',
|
||||
'./tailwind.config.js',
|
||||
],
|
||||
defaultConfigFile: './tailwind.config.mjs',
|
||||
defaultConfigContent: STUBS.TAILWIND_CONFIG,
|
||||
});
|
||||
if (await askToContinue({ flags })) {
|
||||
if (!existsSync(dir)) {
|
||||
await fs.mkdir(dir);
|
||||
}
|
||||
await fs.writeFile(styles, STUBS.TAILWIND_GLOBAL_CSS, 'utf-8');
|
||||
} else {
|
||||
logger.info(
|
||||
'SKIP_FORMAT',
|
||||
`\n @astrojs/tailwind requires additional configuration. Please refer to https://docs.astro.build/en/guides/integrations-guide/tailwind/`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logger.debug('add', `Using existing tailwind configuration`);
|
||||
}
|
||||
}
|
||||
if (integrations.find((integration) => integration.id === 'svelte')) {
|
||||
await setupIntegrationConfig({
|
||||
|
@ -290,6 +288,8 @@ export async function add(names: string[], { flags }: AddOptions) {
|
|||
)}`,
|
||||
);
|
||||
}
|
||||
} else if (integration.id === 'tailwind') {
|
||||
addVitePlugin(mod, 'tailwindcss', '@tailwindcss/vite');
|
||||
} else {
|
||||
addIntegration(mod, integration);
|
||||
}
|
||||
|
@ -358,6 +358,7 @@ export async function add(names: string[], { flags }: AddOptions) {
|
|||
} to your project:\n${list}`,
|
||||
),
|
||||
);
|
||||
logger.info('SKIP_FORMAT', msg.success("Import './src/styles/global.css' in a layout"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,6 +457,31 @@ function addIntegration(mod: ProxifiedModule<any>, integration: IntegrationInfo)
|
|||
}
|
||||
}
|
||||
|
||||
function addVitePlugin(mod: ProxifiedModule<any>, pluginId: string, packageName: string) {
|
||||
const config = getDefaultExportOptions(mod);
|
||||
|
||||
if (!mod.imports.$items.some((imp) => imp.local === pluginId)) {
|
||||
mod.imports.$append({
|
||||
imported: 'default',
|
||||
local: pluginId,
|
||||
from: packageName,
|
||||
});
|
||||
}
|
||||
|
||||
config.vite ??= {};
|
||||
config.vite.plugins ??= [];
|
||||
if (
|
||||
!config.vite.plugins.$ast.elements.some(
|
||||
(el: ASTNode) =>
|
||||
el.type === 'CallExpression' &&
|
||||
el.callee.type === 'Identifier' &&
|
||||
el.callee.name === pluginId,
|
||||
)
|
||||
) {
|
||||
config.vite.plugins.push(builders.functionCall(pluginId));
|
||||
}
|
||||
}
|
||||
|
||||
export function setAdapter(
|
||||
mod: ProxifiedModule<any>,
|
||||
adapter: IntegrationInfo,
|
||||
|
@ -796,6 +822,13 @@ async function validateIntegrations(integrations: string[]): Promise<Integration
|
|||
);
|
||||
}
|
||||
|
||||
if (integration === 'tailwind') {
|
||||
dependencies = [
|
||||
['@tailwindcss/vite', '^4.0.0'],
|
||||
['tailwindcss', '^4.0.0'],
|
||||
];
|
||||
}
|
||||
|
||||
return { id: integration, packageName, dependencies, type: integrationType };
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
configFile: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)),
|
||||
}),
|
||||
],
|
||||
vite: {
|
||||
plugins: [tailwindcss()]
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/tailwind": "workspace:*"
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import './styles/global.css';
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
import NoHoistClassic from '../components/NoHoistClassic.astro';
|
||||
---
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
import NoHoistModule from '../components/NoHoistModule.astro';
|
||||
---
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
import Widget from '../components/Widget.astro';
|
||||
import Widget2 from '../components/Widget2.astro';
|
||||
---
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
const components = await Astro.glob('/src/components/Glob/*');
|
||||
const MyComponent = components[0].default;
|
||||
---
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
import Inline from '../components/Inline.astro';
|
||||
---
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
|
1
packages/astro/test/fixtures/astro-scripts/src/styles/global.css
vendored
Normal file
1
packages/astro/test/fixtures/astro-scripts/src/styles/global.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
@import 'tailwindcss';
|
|
@ -1,8 +0,0 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
configFile: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)),
|
||||
}),
|
||||
],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/tailwind": "workspace:*"
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
---
|
||||
|
||||
<h1 class="text-blue-500 text-2xl font-bold">Hello world</h1>
|
||||
|
||||
<style>
|
||||
|
|
1
packages/astro/test/fixtures/middleware-tailwind/src/styles/global.css
vendored
Normal file
1
packages/astro/test/fixtures/middleware-tailwind/src/styles/global.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
@import 'tailwindcss';
|
|
@ -1,8 +0,0 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -1,17 +1,12 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
|
||||
}),
|
||||
mdx(),
|
||||
],
|
||||
integrations: [mdx()],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/tailwind": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"astro": "workspace:*",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
// Component Imports
|
||||
import Button from '../components/Button.astro';
|
||||
import Complex from '../components/Complex.astro';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import '../styles/global.css';
|
||||
import Button from '../components/Button.astro';
|
||||
import Complex from '../components/Complex.astro';
|
||||
|
||||
|
|
7
packages/astro/test/fixtures/tailwindcss/src/styles/global.css
vendored
Normal file
7
packages/astro/test/fixtures/tailwindcss/src/styles/global.css
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-dawn: #f3e9fa;
|
||||
--color-dusk: #514375;
|
||||
--color-midnight: #31274a;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
const path = require('node:path');
|
||||
|
||||
module.exports = {
|
||||
content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
dawn: '#f3e9fa',
|
||||
dusk: '#514375',
|
||||
midnight: '#31274a',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -373,6 +373,6 @@ describe('Middleware with tailwind', () => {
|
|||
const bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')))
|
||||
.replace(/\s/g, '')
|
||||
.replace('/n', '');
|
||||
assert.equal(bundledCSS.includes('--tw-content'), true);
|
||||
assert.equal(bundledCSS.includes('--tw'), true);
|
||||
});
|
||||
});
|
||||
|
|
550
pnpm-lock.yaml
generated
550
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue