Skip to content

Commit 3176758

Browse files
committed
feat(builder-rsbuild): add fallback rule for ?raw imports
- Add RAW_QUERY_REGEX and appendRules for asset/source imports - Support importing any file as raw string (e.g., .md?raw) - Add RawImport.stories.tsx example in react-18 sandbox - Add unit test for raw query fallback rule
1 parent 796e6d6 commit 3176758

File tree

6 files changed

+93
-7
lines changed

6 files changed

+93
-7
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"typescript.tsdk": "node_modules/typescript/lib",
3+
"editor.codeActionsOnSave": {
4+
"source.organizeImports.biome": "explicit"
5+
},
36
"[javascript]": {
47
"editor.defaultFormatter": "biomejs.biome"
58
},

packages/builder-rsbuild/src/preview/iframe-rsbuild.config.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const builtInResolveExtensions = [
5252
'.cjs',
5353
]
5454

55+
/** @see https://github.com/web-infra-dev/rsbuild/blob/d8204bb72b5dd32dc736372dff6bb618675a4ad5/packages/core/src/constants.ts#L61 */
56+
const RAW_QUERY_REGEX = /[?&]raw(?:&|=|$)/
57+
5558
const globalPath = maybeGetAbsolutePath('@storybook/global')
5659

5760
// these packages are not pre-bundled because of react dependencies.
@@ -299,7 +302,7 @@ export default async (
299302
// Ensure the deconstruction of the in-play function in parameters.
300303
config.env.bugfixes = true
301304
},
302-
rspack: (config, { addRules, rspack, mergeConfig }) => {
305+
rspack: (config, { addRules, appendRules, rspack, mergeConfig }) => {
303306
addRules({
304307
test: /\.stories\.([tj])sx?$|(stories|story)\.mdx$/,
305308
exclude: /node_modules/,
@@ -398,6 +401,16 @@ export default async (
398401
config.lazyCompilation = lazyCompilationConfig
399402
}
400403

404+
// Fallback rule for raw imports (e.g., `import docs from './README.md?raw'`).
405+
// This is a low-priority rule that handles any file with `?raw` query as raw string.
406+
// Placed at the end of rules array via appendRules to ensure it doesn't override
407+
// more specific rules from Rsbuild's asset/script plugins.
408+
// @see https://github.com/storybookjs/storybook/blob/8486c72ce5fc4946755cafdcdb671f6f5dd9937d/code/builders/builder-webpack5/src/preview/base-webpack.config.ts
409+
appendRules({
410+
resourceQuery: RAW_QUERY_REGEX,
411+
type: 'asset/source',
412+
})
413+
401414
return mergeConfig(
402415
config,
403416
extraWebpackConfig || {},

packages/builder-rsbuild/tests/preview/iframe-rsbuild.config.test.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,12 @@ describe('iframe-rsbuild.config', () => {
128128
expect(typeof rspackTool).toBe('function')
129129

130130
const baseConfig = {} as any
131+
const addRules = vi.fn()
132+
const appendRules = vi.fn()
131133

132-
return (rspackTool as any)(baseConfig, {
133-
addRules: vi.fn(),
134+
const result = (rspackTool as any)(baseConfig, {
135+
addRules,
136+
appendRules,
134137
rspack: {
135138
experiments: {
136139
VirtualModulesPlugin: class VirtualModulesPlugin {},
@@ -139,25 +142,37 @@ describe('iframe-rsbuild.config', () => {
139142
},
140143
mergeConfig: (c: any) => c,
141144
}) as any
145+
146+
return { rspackConfig: result, addRules, appendRules }
142147
}
143148

144149
it('uses entries:false when lazyCompilation is unset', async () => {
145-
const rspackConfig = await runRspackTool('unset')
150+
const { rspackConfig } = await runRspackTool('unset')
146151
expect(rspackConfig.lazyCompilation).toEqual({ entries: false })
147152
})
148153

149154
it('disables lazyCompilation when set to false', async () => {
150-
const rspackConfig = await runRspackTool(false)
155+
const { rspackConfig } = await runRspackTool(false)
151156
expect(rspackConfig.lazyCompilation).toBe(false)
152157
})
153158

154159
it('passes through lazyCompilation when set to true', async () => {
155-
const rspackConfig = await runRspackTool(true)
160+
const { rspackConfig } = await runRspackTool(true)
156161
expect(rspackConfig.lazyCompilation).toBe(true)
157162
})
158163

159164
it('passes through lazyCompilation options object', async () => {
160-
const rspackConfig = await runRspackTool({ entries: true })
165+
const { rspackConfig } = await runRspackTool({ entries: true })
161166
expect(rspackConfig.lazyCompilation).toEqual({ entries: true })
162167
})
168+
169+
it('appends raw query fallback rule for asset/source imports', async () => {
170+
const { appendRules } = await runRspackTool(false)
171+
172+
expect(appendRules).toHaveBeenCalledTimes(1)
173+
expect(appendRules).toHaveBeenCalledWith({
174+
resourceQuery: /[?&]raw(?:&|=|$)/,
175+
type: 'asset/source',
176+
})
177+
})
163178
})

sandboxes/react-18/src/env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
/// <reference types="@rsbuild/core/types" />
2+
3+
declare module '*.md?raw' {
4+
const content: string
5+
export default content
6+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { Meta, StoryObj } from '@storybook/react'
2+
import exampleMarkdown from './assets/example.md?raw'
3+
4+
/**
5+
* A simple component that displays raw markdown content.
6+
* This story tests the `?raw` import functionality.
7+
*/
8+
const RawMarkdownDisplay = ({ content }: { content: string }) => {
9+
return (
10+
<pre
11+
style={{
12+
padding: '16px',
13+
backgroundColor: '#f5f5f5',
14+
borderRadius: '4px',
15+
overflow: 'auto',
16+
whiteSpace: 'pre-wrap',
17+
}}
18+
>
19+
{content}
20+
</pre>
21+
)
22+
}
23+
24+
const meta: Meta<typeof RawMarkdownDisplay> = {
25+
title: 'Example/RawImport',
26+
component: RawMarkdownDisplay,
27+
parameters: {
28+
layout: 'padded',
29+
},
30+
}
31+
32+
export default meta
33+
type Story = StoryObj<typeof RawMarkdownDisplay>
34+
35+
/**
36+
* Demonstrates importing a markdown file as raw string using `?raw` query.
37+
*/
38+
export const MarkdownRaw: Story = {
39+
args: {
40+
content: exampleMarkdown,
41+
},
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Example Markdown
2+
3+
This is a simple markdown file used to test the `?raw` import functionality.
4+
5+
## Features
6+
7+
- Raw imports work correctly
8+
- MDX can import markdown as raw strings

0 commit comments

Comments
 (0)