Skip to content

Commit 93fd6bc

Browse files
authored
feat: support tailwindcssPath option to specify the tailwindcss (#56)
In some cases such as in monorepo, the `tailwindcss` isn't installed in current root path or can't be resolved correctly. Plugin need to provide a option to specify the path of `tailwindcss`.
1 parent aa9ee69 commit 93fd6bc

File tree

5 files changed

+123
-6
lines changed

5 files changed

+123
-6
lines changed

src/TailwindCSSRspackPlugin.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ interface TailwindRspackPluginOptions {
144144
PostCSSLoaderOptions['postcssOptions'],
145145
(loaderContext: Rspack.LoaderContext) => void
146146
>;
147+
148+
/**
149+
* Specifies the absolute path to the tailwindcss package.
150+
*
151+
* By default, tailwindcss is resolved using Node.js module resolution algorithm
152+
* starting from the project's root directory. This option allows explicit
153+
* specification of the tailwindcss location for scenarios where automatic
154+
* resolution fails or the resolved path is not correct, such as in monorepo.
155+
*
156+
* ```js
157+
* // rspack.config.js
158+
* import { TailwindRspackPlugin } from 'rsbuild-plugin-tailwindcss'
159+
*
160+
* export default {
161+
* plugins: [
162+
* new TailwindRspackPlugin({
163+
* postcssOptions: {
164+
* plugins: {
165+
* tailwindcssPath: require.resolve('tailwindcss'),
166+
* },
167+
* },
168+
* }),
169+
* ],
170+
* }
171+
* ```
172+
*/
173+
tailwindcssPath?: string | undefined;
147174
}
148175

149176
// From `@rollup/pluginutils`
@@ -244,14 +271,17 @@ class TailwindRspackPluginImpl {
244271
return;
245272
}
246273
}
247-
248274
const [
249275
{ default: postcss },
250276
{ default: tailwindcss },
251277
configPath,
252278
] = await Promise.all([
253279
import('postcss'),
254-
import('tailwindcss'),
280+
import(
281+
typeof this.options.tailwindcssPath === 'string'
282+
? `${pathToFileURL(this.options.tailwindcssPath)}`
283+
: 'tailwindcss'
284+
),
255285
this.#prepareTailwindConfig(
256286
entryName,
257287
Array.from(entryModules).filter(filter),
@@ -335,6 +365,7 @@ class TailwindRspackPluginImpl {
335365
const [configName, configContent] = await this.#generateTailwindConfig(
336366
userConfig,
337367
entryModules,
368+
this.options.tailwindcssPath,
338369
);
339370
const configPath = path.resolve(outputDir, configName);
340371

@@ -343,10 +374,12 @@ class TailwindRspackPluginImpl {
343374
return configPath;
344375
}
345376

346-
async #resolveTailwindCSSVersion(): Promise<string> {
377+
async #resolveTailwindCSSVersion(
378+
tailwindcssPath: string | undefined,
379+
): Promise<string> {
347380
const require = createRequire(import.meta.url);
348381
const pkgPath = require.resolve('tailwindcss/package.json', {
349-
paths: [this.compiler.context],
382+
paths: [tailwindcssPath ? path.dirname(tailwindcssPath) : this.compiler.context],
350383
});
351384

352385
const content = await readFile(pkgPath, 'utf-8');
@@ -359,8 +392,9 @@ class TailwindRspackPluginImpl {
359392
async #generateTailwindConfig(
360393
userConfig: string,
361394
entryModules: string[],
395+
tailwindcssPath: string | undefined,
362396
): Promise<['tailwind.config.mjs' | 'tailwind.config.cjs', string]> {
363-
const version = await this.#resolveTailwindCSSVersion();
397+
const version = await this.#resolveTailwindCSSVersion(tailwindcssPath);
364398

365399
const { default: satisfies } = await import(
366400
'semver/functions/satisfies.js'

src/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ export interface PluginTailwindCSSOptions {
112112
* ```
113113
*/
114114
include?: FilterPattern | undefined;
115+
116+
/**
117+
* Specifies the absolute path to the tailwindcss package.
118+
*
119+
* By default, tailwindcss is resolved using Node.js module resolution algorithm
120+
* starting from the project's root directory. This option allows explicit
121+
* specification of the tailwindcss location for scenarios where automatic
122+
* resolution fails or the resolved path is not correct, such as in monorepo.
123+
*
124+
* ```js
125+
* // rspack.config.js
126+
* import { TailwindRspackPlugin } from 'rsbuild-plugin-tailwindcss'
127+
*
128+
* export default {
129+
* plugins: [
130+
* new TailwindRspackPlugin({
131+
* postcssOptions: {
132+
* plugins: {
133+
* tailwindcssPath: require.resolve('tailwindcss'),
134+
* },
135+
* },
136+
* }),
137+
* ],
138+
* }
139+
* ```
140+
*/
141+
tailwindcssPath?: string;
115142
}
116143

117144
export const pluginTailwindCSS = (
@@ -161,6 +188,7 @@ export const pluginTailwindCSS = (
161188
include: options.include,
162189
exclude: options.exclude,
163190
postcssOptions,
191+
tailwindcssPath: options.tailwindcssPath,
164192
},
165193
]);
166194
},

test/config/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
33
import { expect, test } from '@playwright/test';
44
import { createRsbuild } from '@rsbuild/core';
55
import { pluginTailwindCSS } from '../../src';
6-
import { supportESM, getRandomPort } from '../helper';
6+
import { getRandomPort, supportESM } from '../helper';
77

88
const __dirname = dirname(fileURLToPath(import.meta.url));
99

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { createRequire } from 'node:module';
2+
import { dirname } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
import { expect, test } from '@playwright/test';
5+
import { createRsbuild } from '@rsbuild/core';
6+
import { pluginTailwindCSS } from '../../src';
7+
import { getRandomPort } from '../helper';
8+
9+
const __dirname = dirname(fileURLToPath(import.meta.url));
10+
const require = createRequire(import.meta.url);
11+
12+
test('should resolve tailwindcss', async ({ page }) => {
13+
const rsbuild = await createRsbuild({
14+
cwd: __dirname,
15+
rsbuildConfig: {
16+
plugins: [
17+
pluginTailwindCSS({
18+
tailwindcssPath: require.resolve('tailwindcss'),
19+
}),
20+
],
21+
server: {
22+
port: getRandomPort(),
23+
},
24+
},
25+
});
26+
27+
const { server, urls } = await rsbuild.startDevServer();
28+
29+
await page.goto(urls[0]);
30+
31+
const display = await page.evaluate(() => {
32+
const el = document.getElementById('test');
33+
34+
if (!el) {
35+
throw new Error('#test not found');
36+
}
37+
38+
return window.getComputedStyle(el).getPropertyValue('margin');
39+
});
40+
41+
expect(display).toBe('0px');
42+
43+
await server.close();
44+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'tailwindcss/utilities.css';
2+
3+
function className() {
4+
return 'm-0';
5+
}
6+
7+
const root = document.getElementById('root');
8+
const element = document.createElement('div');
9+
element.id = 'test';
10+
element.className = className();
11+
root.appendChild(element);

0 commit comments

Comments
 (0)