diff --git a/.changeset/real-spies-pretend.md b/.changeset/real-spies-pretend.md new file mode 100644 index 000000000000..009585c7c14b --- /dev/null +++ b/.changeset/real-spies-pretend.md @@ -0,0 +1,34 @@ +--- +'@astrojs/tailwind': major +--- + +Let tailwind postcss plugin load its config file itself. This changes the `tailwind.config.js` loading behaviour where Tailwind would load the config file from `process.cwd()` instead of the project `root`. You can configure the integration's `config.path` option to load from a specific path instead. + +```js +import { defineConfig } from 'astro/config'; +import tailwind from '@astrojs/tailwind'; +import { fileURLToPath } from 'url'; + +export default defineConfig({ + integrations: [ + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)), + }, + }), + ], +}); +``` + +This change also requires a Tailwind config file to exist in your project as Astro's fallback value is no longer provided. It is set up automatically during `astro add tailwind`, but you can also manually create a `tailwind.config.cjs` file in your project root: + +```js +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: {}, + }, + plugins: [], +} +``` diff --git a/packages/astro/e2e/fixtures/tailwindcss/astro.config.mjs b/packages/astro/e2e/fixtures/tailwindcss/astro.config.mjs index 473be9666e1e..681ec1bc25ba 100644 --- a/packages/astro/e2e/fixtures/tailwindcss/astro.config.mjs +++ b/packages/astro/e2e/fixtures/tailwindcss/astro.config.mjs @@ -1,9 +1,16 @@ import { defineConfig } from 'astro/config'; import tailwind from '@astrojs/tailwind'; +import { fileURLToPath } from 'url'; // https://astro.build/config export default defineConfig({ - integrations: [tailwind()], + integrations: [ + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)), + }, + }), + ], vite: { build: { assetsInlineLimit: 0, diff --git a/packages/astro/e2e/fixtures/tailwindcss/postcss.config.js b/packages/astro/e2e/fixtures/tailwindcss/postcss.config.js deleted file mode 100644 index 7df5ecb39683..000000000000 --- a/packages/astro/e2e/fixtures/tailwindcss/postcss.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('path'); -module.exports = { - plugins: { - tailwindcss: { - config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs! - }, - autoprefixer: {} - }, -}; diff --git a/packages/astro/test/astro-scripts.test.js b/packages/astro/test/astro-scripts.test.js index da3fc7c6660d..ae2268d8097f 100644 --- a/packages/astro/test/astro-scripts.test.js +++ b/packages/astro/test/astro-scripts.test.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import * as cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; -import { tailwind } from './fixtures/astro-scripts/deps.mjs'; describe('Scripts (hoisted and not)', () => { describe('Build', () => { @@ -141,7 +140,6 @@ describe('Scripts (hoisted and not)', () => { fixture = await loadFixture({ root: './fixtures/astro-scripts/', integrations: [ - tailwind(), { name: 'test-script-injection-with-injected-route', hooks: { diff --git a/packages/astro/test/fixtures/astro-scripts/astro.config.mjs b/packages/astro/test/fixtures/astro-scripts/astro.config.mjs index e841c915cfed..8e44acd75f44 100644 --- a/packages/astro/test/fixtures/astro-scripts/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-scripts/astro.config.mjs @@ -1,8 +1,13 @@ import { defineConfig } from 'astro/config'; import tailwind from '@astrojs/tailwind'; +import { fileURLToPath } from 'url'; export default defineConfig({ integrations: [ - tailwind() - ] -}) + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)), + }, + }), + ], +}); diff --git a/packages/astro/test/fixtures/astro-scripts/deps.mjs b/packages/astro/test/fixtures/astro-scripts/deps.mjs deleted file mode 100644 index e6a090e7ba97..000000000000 --- a/packages/astro/test/fixtures/astro-scripts/deps.mjs +++ /dev/null @@ -1,2 +0,0 @@ -export { default as tailwind } from '@astrojs/tailwind'; - diff --git a/packages/astro/test/fixtures/astro-scripts/tailwind.config.cjs b/packages/astro/test/fixtures/astro-scripts/tailwind.config.cjs new file mode 100644 index 000000000000..f5368a76ab04 --- /dev/null +++ b/packages/astro/test/fixtures/astro-scripts/tailwind.config.cjs @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/packages/astro/test/fixtures/middleware-tailwind/astro.config.mjs b/packages/astro/test/fixtures/middleware-tailwind/astro.config.mjs index db58bbf858ea..73ece68e02b7 100644 --- a/packages/astro/test/fixtures/middleware-tailwind/astro.config.mjs +++ b/packages/astro/test/fixtures/middleware-tailwind/astro.config.mjs @@ -1,8 +1,14 @@ import { defineConfig } from 'astro/config'; import tailwind from '@astrojs/tailwind'; - +import { fileURLToPath } from 'url'; // https://astro.build/config export default defineConfig({ - integrations: [tailwind()], + integrations: [ + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)), + }, + }), + ], }); diff --git a/packages/astro/test/fixtures/middleware-tailwind/tailwind.config.cjs b/packages/astro/test/fixtures/middleware-tailwind/tailwind.config.cjs new file mode 100644 index 000000000000..f5368a76ab04 --- /dev/null +++ b/packages/astro/test/fixtures/middleware-tailwind/tailwind.config.cjs @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts b/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts index 0a5f36a87f53..936426736216 100644 --- a/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts +++ b/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts @@ -1,7 +1,14 @@ import { defineConfig } from 'astro/config'; -import tailwind from "@astrojs/tailwind"; +import tailwind from '@astrojs/tailwind'; +import { fileURLToPath } from 'url'; // https://astro.build/config export default defineConfig({ - integrations: [tailwind()] -}); \ No newline at end of file + integrations: [ + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)), + }, + }), + ], +}); diff --git a/packages/astro/test/fixtures/tailwindcss/astro.config.mjs b/packages/astro/test/fixtures/tailwindcss/astro.config.mjs index 8274fd3b9e58..390bd3b73504 100644 --- a/packages/astro/test/fixtures/tailwindcss/astro.config.mjs +++ b/packages/astro/test/fixtures/tailwindcss/astro.config.mjs @@ -1,10 +1,18 @@ import { defineConfig } from 'astro/config'; import tailwind from '@astrojs/tailwind'; import mdx from '@astrojs/mdx'; +import { fileURLToPath } from 'url'; // https://astro.build/config export default defineConfig({ - integrations: [tailwind(), mdx()], + integrations: [ + tailwind({ + config: { + path: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)), + }, + }), + mdx(), + ], vite: { build: { assetsInlineLimit: 0, diff --git a/packages/astro/test/fixtures/tailwindcss/postcss.config.js b/packages/astro/test/fixtures/tailwindcss/postcss.config.js deleted file mode 100644 index 779a7e47fb53..000000000000 --- a/packages/astro/test/fixtures/tailwindcss/postcss.config.js +++ /dev/null @@ -1,10 +0,0 @@ -const path = require('path'); - -module.exports = { - plugins: { - tailwindcss: { - config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs! - }, - autoprefixer: {} - }, -}; diff --git a/packages/integrations/tailwind/package.json b/packages/integrations/tailwind/package.json index 750962842e6f..e1349e01024e 100644 --- a/packages/integrations/tailwind/package.json +++ b/packages/integrations/tailwind/package.json @@ -32,9 +32,8 @@ "dev": "astro-scripts dev \"src/**/*.ts\"" }, "dependencies": { - "@proload/core": "^0.3.3", "autoprefixer": "^10.4.14", - "postcss": "^8.4.23", + "postcss": "^8.4.24", "postcss-load-config": "^4.0.1" }, "devDependencies": { diff --git a/packages/integrations/tailwind/src/index.ts b/packages/integrations/tailwind/src/index.ts index 04be569b9470..d489c019684f 100644 --- a/packages/integrations/tailwind/src/index.ts +++ b/packages/integrations/tailwind/src/index.ts @@ -1,72 +1,9 @@ -import load, { resolve } from '@proload/core'; -import type { AstroConfig, AstroIntegration } from 'astro'; +import type { AstroIntegration } from 'astro'; import autoprefixerPlugin from 'autoprefixer'; -import fs from 'fs/promises'; -import path from 'path'; -import tailwindPlugin, { type Config as TailwindConfig } from 'tailwindcss'; -import resolveConfig from 'tailwindcss/resolveConfig.js'; -import { fileURLToPath } from 'url'; +import type { ResultPlugin } from 'postcss-load-config'; +import tailwindPlugin from 'tailwindcss'; import type { CSSOptions, UserConfig } from 'vite'; -function getDefaultTailwindConfig(srcUrl: URL): TailwindConfig { - return resolveConfig({ - theme: { - extend: {}, - }, - plugins: [], - content: [path.join(fileURLToPath(srcUrl), `**`, `*.{astro,html,js,jsx,svelte,ts,tsx,vue}`)], - presets: undefined, // enable Tailwind's default preset - }) as TailwindConfig; -} - -async function getUserConfig(root: URL, configPath?: string, isRestart = false) { - const resolvedRoot = fileURLToPath(root); - let userConfigPath: string | undefined; - - if (configPath) { - const configPathWithLeadingSlash = /^\.*\//.test(configPath) ? configPath : `./${configPath}`; - userConfigPath = fileURLToPath(new URL(configPathWithLeadingSlash, root)); - } - - if (isRestart) { - // Hack: Write config to temporary file at project root - // This invalidates and reloads file contents when using ESM imports or "resolve" - const resolvedConfigPath = (await resolve('tailwind', { - mustExist: false, - cwd: resolvedRoot, - filePath: userConfigPath, - })) as string; - - const { dir, base } = path.parse(resolvedConfigPath); - const tempConfigPath = path.join(dir, `.temp.${Date.now()}.${base}`); - await fs.copyFile(resolvedConfigPath, tempConfigPath); - - let result: load.Config> | undefined; - try { - result = await load('tailwind', { - mustExist: false, - cwd: resolvedRoot, - filePath: tempConfigPath, - }); - } catch (err) { - console.error(err); - } finally { - await fs.unlink(tempConfigPath); - } - - return { - ...result, - filePath: resolvedConfigPath, - }; - } else { - return await load('tailwind', { - mustExist: false, - cwd: resolvedRoot, - filePath: userConfigPath, - }); - } -} - async function getPostCssConfig( root: UserConfig['root'], postcssInlineOptions: CSSOptions['postcss'] @@ -86,20 +23,19 @@ async function getPostCssConfig( } async function getViteConfiguration( - tailwindConfig: TailwindConfig, - viteConfig: AstroConfig['vite'] + tailwindConfigPath: string | undefined, + viteConfig: UserConfig ) { // We need to manually load postcss config files because when inlining the tailwind and autoprefixer plugins, // that causes vite to ignore postcss config files const postcssConfigResult = await getPostCssConfig(viteConfig.root, viteConfig.css?.postcss); - const postcssOptions = (postcssConfigResult && postcssConfigResult.options) || {}; - - const postcssPlugins = - postcssConfigResult && postcssConfigResult.plugins ? postcssConfigResult.plugins.slice() : []; - postcssPlugins.push(tailwindPlugin(tailwindConfig)); + const postcssOptions = postcssConfigResult?.options ?? {}; + const postcssPlugins = postcssConfigResult?.plugins?.slice() ?? []; + postcssPlugins.push(tailwindPlugin(tailwindConfigPath) as ResultPlugin); postcssPlugins.push(autoprefixerPlugin()); + return { css: { postcss: { @@ -123,7 +59,7 @@ type TailwindOptions = * Disabling this is useful when further customization of Tailwind styles * and directives is required. See {@link https://tailwindcss.com/docs/functions-and-directives#tailwind Tailwind's docs} * for more details on directives and customization. - * @default: true + * @default true */ applyBaseStyles?: boolean; }; @@ -136,33 +72,10 @@ export default function tailwindIntegration(options?: TailwindOptions): AstroInt return { name: '@astrojs/tailwind', hooks: { - 'astro:config:setup': async ({ - config, - updateConfig, - injectScript, - addWatchFile, - isRestart, - }) => { + 'astro:config:setup': async ({ config, updateConfig, injectScript }) => { // Inject the Tailwind postcss plugin - const userConfig = await getUserConfig(config.root, customConfigPath, isRestart); - - if (customConfigPath && !userConfig?.value) { - throw new Error( - `Could not find a Tailwind config at ${JSON.stringify( - customConfigPath - )}. Does the file exist?` - ); - } - - if (addWatchFile && userConfig?.filePath) { - addWatchFile(userConfig.filePath); - } - - const tailwindConfig = - (userConfig?.value as TailwindConfig) ?? getDefaultTailwindConfig(config.srcDir); - updateConfig({ - vite: await getViteConfiguration(tailwindConfig, config.vite), + vite: await getViteConfiguration(customConfigPath, config.vite), }); if (applyBaseStyles) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47bf785694c7..548b8b4d9887 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4400,7 +4400,7 @@ importers: version: 9.2.2 vite: specifier: ^4.3.1 - version: 4.3.1(@types/node@18.16.3)(sass@1.52.2) + version: 4.3.1(@types/node@14.18.21) packages/integrations/netlify/test/edge-functions/fixtures/dynimport: dependencies: @@ -4787,18 +4787,15 @@ importers: packages/integrations/tailwind: dependencies: - '@proload/core': - specifier: ^0.3.3 - version: 0.3.3 autoprefixer: specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.23) + version: 10.4.14(postcss@8.4.24) postcss: - specifier: ^8.4.23 - version: 8.4.23 + specifier: ^8.4.24 + version: 8.4.24 postcss-load-config: specifier: ^4.0.1 - version: 4.0.1(postcss@8.4.23) + version: 4.0.1(postcss@8.4.24) devDependencies: astro: specifier: workspace:* @@ -4918,7 +4915,7 @@ importers: version: 3.0.0(vite@4.3.1)(vue@3.2.47) '@vue/babel-plugin-jsx': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.21.8) + version: 1.1.1 '@vue/compiler-sfc': specifier: ^3.2.39 version: 3.2.39 @@ -8397,13 +8394,6 @@ packages: preact: 10.13.2 dev: false - /@proload/core@0.3.3: - resolution: {integrity: sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ==} - dependencies: - deepmerge: 4.3.1 - escalade: 3.1.1 - dev: false - /@rollup/plugin-alias@3.1.9(rollup@2.79.1): resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} engines: {node: '>=8.0.0'} @@ -9312,6 +9302,23 @@ packages: resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==} dev: false + /@vue/babel-plugin-jsx@1.1.1: + resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==} + dependencies: + '@babel/helper-module-imports': 7.21.4 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.18.2) + '@babel/template': 7.20.7 + '@babel/traverse': 7.18.2 + '@babel/types': 7.21.5 + '@vue/babel-helper-vue-transform-on': 1.0.2 + camelcase: 6.3.0 + html-tags: 3.3.1 + svg-tags: 1.0.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: false + /@vue/babel-plugin-jsx@1.1.1(@babel/core@7.21.8): resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==} dependencies: @@ -9370,7 +9377,7 @@ packages: '@vue/shared': 3.2.39 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.23 + postcss: 8.4.24 source-map: 0.6.1 dev: false @@ -9385,7 +9392,7 @@ packages: '@vue/shared': 3.2.47 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.23 + postcss: 8.4.24 source-map: 0.6.1 /@vue/compiler-ssr@3.2.39: @@ -9784,6 +9791,22 @@ packages: postcss: 8.4.23 postcss-value-parser: 4.2.0 + /autoprefixer@10.4.14(postcss@8.4.24): + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001487 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + dev: false + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -15239,6 +15262,23 @@ packages: postcss: 8.4.23 yaml: 2.2.2 + /postcss-load-config@4.0.1(postcss@8.4.24): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.24 + yaml: 2.2.2 + dev: false + /postcss-logical@5.0.4(postcss@8.4.23): resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} engines: {node: ^12 || ^14 || >=16} @@ -15416,6 +15456,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.24: + resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /preact-render-to-string@5.2.4(preact@10.13.2): resolution: {integrity: sha512-iIPHb3BXUQ3Za6KNhkjN/waq11Oh+QWWtAgN3id3LrL+cszH3DYh8TxJPNQ6Aogsbu4JsqdJLBZltwPFpG6N6w==} peerDependencies: @@ -17602,6 +17650,39 @@ packages: - supports-color dev: false + /vite@4.3.1(@types/node@14.18.21): + resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 14.18.21 + esbuild: 0.17.18 + postcss: 8.4.23 + rollup: 3.21.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vite@4.3.1(@types/node@18.16.3)(sass@1.52.2): resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} engines: {node: ^14.18.0 || >=16.0.0}