Skip to content

Commit 6c88499

Browse files
authored
feat(plugin-less): add support for inline query (#4859)
1 parent 6005737 commit 6c88499

File tree

17 files changed

+367
-75
lines changed

17 files changed

+367
-75
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { build, dev, rspackOnlyTest } from '@e2e/helper';
2+
import { expect } from '@playwright/test';
3+
4+
rspackOnlyTest(
5+
'should allow to import inline Less files in development mode',
6+
async ({ page }) => {
7+
const rsbuild = await dev({
8+
cwd: __dirname,
9+
page,
10+
});
11+
12+
const aInline: string = await page.evaluate('window.aInline');
13+
const bInline: string = await page.evaluate('window.bInline');
14+
15+
expect(
16+
aInline.includes('.header-class') && aInline.includes('color: red'),
17+
).toBe(true);
18+
expect(
19+
bInline.includes('.title-class') && bInline.includes('font-size: 14px'),
20+
).toBe(true);
21+
22+
await rsbuild.close();
23+
},
24+
);
25+
26+
rspackOnlyTest(
27+
'should allow to import inline Less files in production mode',
28+
async ({ page }) => {
29+
const rsbuild = await build({
30+
cwd: __dirname,
31+
page,
32+
});
33+
34+
expect(await page.evaluate('window.aInline')).toBe(
35+
'.header-class{color:red}',
36+
);
37+
expect(await page.evaluate('window.bInline')).toBe(
38+
'.title-class{font-size:14px}',
39+
);
40+
41+
await rsbuild.close();
42+
},
43+
);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import aInline from './a.less?inline';
2+
import bInline from './b.module.less?inline';
3+
4+
window.aInline = aInline;
5+
window.bInline = bInline;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { pluginLess } from '@rsbuild/plugin-less';
2+
3+
export default {
4+
plugins: [pluginLess()],
5+
};

e2e/cases/less/raw-query/src/a.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.header {
2+
&-class {
3+
color: red;
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.title {
2+
&-class {
3+
font-size: 14px;
4+
}
5+
}
Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
1-
import { readFileSync } from 'node:fs';
2-
import path from 'node:path';
3-
import { build, dev } from '@e2e/helper';
4-
import { expect, test } from '@playwright/test';
1+
import { build, dev, rspackOnlyTest } from '@e2e/helper';
2+
import { expect } from '@playwright/test';
53

6-
test('should allow to import inline Sass files in development mode', async ({
7-
page,
8-
}) => {
9-
const rsbuild = await dev({
10-
cwd: __dirname,
11-
page,
12-
});
4+
rspackOnlyTest(
5+
'should allow to import inline Sass files in development mode',
6+
async ({ page }) => {
7+
const rsbuild = await dev({
8+
cwd: __dirname,
9+
page,
10+
});
1311

14-
const aInline: string = await page.evaluate('window.aInline');
15-
const bInline: string = await page.evaluate('window.bInline');
12+
const aInline: string = await page.evaluate('window.aInline');
13+
const bInline: string = await page.evaluate('window.bInline');
1614

17-
expect(
18-
aInline.includes('.header-class') && aInline.includes('color: red'),
19-
).toBe(true);
20-
expect(
21-
bInline.includes('.title-class') && bInline.includes('font-size: 14px'),
22-
).toBe(true);
15+
expect(
16+
aInline.includes('.header-class') && aInline.includes('color: red'),
17+
).toBe(true);
18+
expect(
19+
bInline.includes('.title-class') && bInline.includes('font-size: 14px'),
20+
).toBe(true);
2321

24-
await rsbuild.close();
25-
});
22+
await rsbuild.close();
23+
},
24+
);
2625

27-
test('should allow to import inline Sass files in production mode', async ({
28-
page,
29-
}) => {
30-
const rsbuild = await build({
31-
cwd: __dirname,
32-
page,
33-
});
26+
rspackOnlyTest(
27+
'should allow to import inline Sass files in production mode',
28+
async ({ page }) => {
29+
const rsbuild = await build({
30+
cwd: __dirname,
31+
page,
32+
});
3433

35-
expect(await page.evaluate('window.aInline')).toBe(
36-
'.header-class{color:red}',
37-
);
38-
expect(await page.evaluate('window.bInline')).toBe(
39-
'.title-class{font-size:14px}',
40-
);
34+
expect(await page.evaluate('window.aInline')).toBe(
35+
'.header-class{color:red}',
36+
);
37+
expect(await page.evaluate('window.bInline')).toBe(
38+
'.title-class{font-size:14px}',
39+
);
4140

42-
await rsbuild.close();
43-
});
41+
await rsbuild.close();
42+
},
43+
);

packages/core/src/configChain.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export const CHAIN_ID = {
5757
LESS: 'less',
5858
/** Rule for raw Less */
5959
LESS_RAW: 'less-raw',
60+
/** Rule for inline Less */
61+
LESS_INLINE: 'less-inline',
6062
/** Rule for Sass */
6163
SASS: 'sass',
6264
/** Rule for raw Sass */

packages/core/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ declare module '*.sass?inline' {
199199
const content: string;
200200
export default content;
201201
}
202+
declare module '*.less?inline' {
203+
const content: string;
204+
export default content;
205+
}
202206

203207
/**
204208
* Raw CSS

packages/plugin-less/src/index.ts

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,29 @@ export const pluginLess = (
135135
name: PLUGIN_LESS_NAME,
136136

137137
setup(api) {
138+
const { include = /\.less$/ } = pluginOptions;
139+
138140
api.modifyBundlerChain(async (chain, { CHAIN_ID, environment }) => {
139141
const { config } = environment;
140142

141-
const ruleId = findRuleId(chain, CHAIN_ID.RULE.LESS);
142-
const test = pluginOptions.include ?? /\.less$/;
143143
const rule = chain.module
144-
.rule(ruleId)
145-
.test(test)
144+
.rule(findRuleId(chain, CHAIN_ID.RULE.LESS))
145+
.test(include)
146+
// exclude `import './foo.less?raw'` and `import './foo.less?inline'`
147+
.resourceQuery({ not: /raw|inline/ })
146148
.sideEffects(true)
147149
.resolve.preferRelative(true)
148-
.end()
149-
// exclude `import './foo.less?raw'`
150-
.resourceQuery({ not: /raw/ });
150+
.end();
151+
152+
const inlineRule = chain.module
153+
.rule(findRuleId(chain, CHAIN_ID.RULE.LESS_INLINE))
154+
.test(include)
155+
.resourceQuery(/inline/);
151156

152157
// Support for importing raw Less files
153158
chain.module
154159
.rule(CHAIN_ID.RULE.LESS_RAW)
155-
.test(test)
160+
.test(include)
156161
.type('asset/source')
157162
.resourceQuery(/raw/);
158163

@@ -163,35 +168,48 @@ export const pluginLess = (
163168
api.context.rootPath,
164169
);
165170

166-
for (const item of excludes) {
167-
rule.exclude.add(item);
168-
}
171+
// Update the normal rule and the inline rule
172+
const updateRules = (
173+
callback: (rule: RspackChain.Rule, type: 'normal' | 'inline') => void,
174+
) => {
175+
callback(rule, 'normal');
176+
callback(inlineRule, 'inline');
177+
};
178+
179+
const lessLoaderPath = path.join(
180+
__dirname,
181+
'../compiled/less-loader/index.js',
182+
);
169183

170-
if (pluginOptions.exclude) {
171-
rule.exclude.add(pluginOptions.exclude);
172-
}
184+
updateRules((rule, type) => {
185+
for (const item of excludes) {
186+
rule.exclude.add(item);
187+
}
188+
if (pluginOptions.exclude) {
189+
rule.exclude.add(pluginOptions.exclude);
190+
}
173191

174-
// Copy the builtin CSS rules
175-
const cssRule = chain.module.rules.get(CHAIN_ID.RULE.CSS);
176-
rule.dependency(cssRule.get('dependency'));
192+
// Copy the builtin CSS rules
193+
const cssRule = chain.module.rules.get(
194+
type === 'normal' ? CHAIN_ID.RULE.CSS : CHAIN_ID.RULE.CSS_INLINE,
195+
);
196+
rule.dependency(cssRule.get('dependency'));
177197

178-
for (const id of Object.keys(cssRule.uses.entries())) {
179-
const loader = cssRule.uses.get(id);
180-
const options = loader.get('options') ?? {};
181-
const clonedOptions = deepmerge<Record<string, any>>({}, options);
198+
for (const id of Object.keys(cssRule.uses.entries())) {
199+
const loader = cssRule.uses.get(id);
200+
const options = loader.get('options') ?? {};
201+
const clonedOptions = deepmerge<Record<string, any>>({}, options);
182202

183-
if (id === CHAIN_ID.USE.CSS) {
184-
// add less-loader
185-
clonedOptions.importLoaders += 1;
186-
}
203+
if (id === CHAIN_ID.USE.CSS) {
204+
// add less-loader
205+
clonedOptions.importLoaders += 1;
206+
}
187207

188-
rule.use(id).loader(loader.get('loader')).options(clonedOptions);
189-
}
208+
rule.use(id).loader(loader.get('loader')).options(clonedOptions);
209+
}
190210

191-
rule
192-
.use(CHAIN_ID.USE.LESS)
193-
.loader(path.join(__dirname, '../compiled/less-loader/index.js'))
194-
.options(options);
211+
rule.use(CHAIN_ID.USE.LESS).loader(lessLoaderPath).options(options);
212+
});
195213
});
196214
},
197215
});

0 commit comments

Comments
 (0)