index.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { fileURLToPath } from 'node:url';
  2. import type { AstroIntegration } from 'astro';
  3. import autoprefixerPlugin from 'autoprefixer';
  4. import tailwindPlugin from 'tailwindcss';
  5. import type { CSSOptions, UserConfig } from 'vite';
  6. async function getPostCssConfig(
  7. root: UserConfig['root'],
  8. postcssInlineOptions: CSSOptions['postcss']
  9. ) {
  10. let postcssConfigResult;
  11. // Check if postcss config is not inlined
  12. if (!(typeof postcssInlineOptions === 'object' && postcssInlineOptions !== null)) {
  13. let { default: postcssrc } = await import('postcss-load-config');
  14. const searchPath = typeof postcssInlineOptions === 'string' ? postcssInlineOptions : root!;
  15. try {
  16. postcssConfigResult = await postcssrc({}, searchPath);
  17. } catch (e) {
  18. postcssConfigResult = null;
  19. }
  20. }
  21. return postcssConfigResult;
  22. }
  23. async function getViteConfiguration(
  24. tailwindConfigPath: string | undefined,
  25. nesting: boolean,
  26. root: string,
  27. postcssInlineOptions: CSSOptions['postcss']
  28. ): Promise<Partial<UserConfig>> {
  29. // We need to manually load postcss config files because when inlining the tailwind and autoprefixer plugins,
  30. // that causes vite to ignore postcss config files
  31. const postcssConfigResult = await getPostCssConfig(root, postcssInlineOptions);
  32. const postcssOptions = postcssConfigResult?.options ?? {};
  33. const postcssPlugins = postcssConfigResult?.plugins?.slice() ?? [];
  34. if (nesting) {
  35. const tailwindcssNestingPlugin = (await import('tailwindcss/nesting/index.js')).default;
  36. postcssPlugins.push(tailwindcssNestingPlugin());
  37. }
  38. postcssPlugins.push(tailwindPlugin(tailwindConfigPath));
  39. postcssPlugins.push(autoprefixerPlugin());
  40. return {
  41. css: {
  42. postcss: {
  43. ...postcssOptions,
  44. plugins: postcssPlugins,
  45. },
  46. },
  47. };
  48. }
  49. type TailwindOptions = {
  50. /**
  51. * Path to your tailwind config file
  52. * @default 'tailwind.config.mjs'
  53. */
  54. configFile?: string;
  55. /**
  56. * Apply Tailwind's base styles
  57. * Disabling this is useful when further customization of Tailwind styles
  58. * and directives is required. See {@link https://tailwindcss.com/docs/functions-and-directives#tailwind Tailwind's docs}
  59. * for more details on directives and customization.
  60. * @default true
  61. */
  62. applyBaseStyles?: boolean;
  63. /**
  64. * Add CSS nesting support using `tailwindcss/nesting`. See {@link https://tailwindcss.com/docs/using-with-preprocessors#nesting Tailwind's docs}
  65. * for how this works with `postcss-nesting` and `postcss-nested`.
  66. */
  67. nesting?: boolean;
  68. };
  69. export default function tailwindIntegration(options?: TailwindOptions): AstroIntegration {
  70. const applyBaseStyles = options?.applyBaseStyles ?? true;
  71. const customConfigPath = options?.configFile;
  72. const nesting = options?.nesting ?? false;
  73. return {
  74. name: '@astrojs/tailwind',
  75. hooks: {
  76. 'astro:config:setup': async ({ config, updateConfig, injectScript }) => {
  77. // Inject the Tailwind postcss plugin
  78. updateConfig({
  79. vite: await getViteConfiguration(
  80. customConfigPath,
  81. nesting,
  82. fileURLToPath(config.root),
  83. config.vite.css?.postcss
  84. ),
  85. });
  86. if (applyBaseStyles) {
  87. // Inject the Tailwind base import
  88. injectScript('page-ssr', `import '@astrojs/tailwind/base.css';`);
  89. }
  90. },
  91. },
  92. };
  93. }