Skip to content

Commit 6649b6a

Browse files
committed
fix: use native plugin to handle hashbang and react directives
1 parent 5d95e2f commit 6649b6a

File tree

13 files changed

+62
-215
lines changed

13 files changed

+62
-215
lines changed

packages/core/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ export function composeMinifyConfig(config: LibConfig): EnvironmentConfig {
414414
// MF assets are loaded over the network, which means they will not be compressed by the project. Therefore, minifying them is necessary.
415415
minify: format === 'mf',
416416
compress: {
417+
directives: false,
417418
defaults: false,
418419
unused: true,
419420
dead_code: true,
@@ -1831,6 +1832,7 @@ async function composeLibRsbuildConfig(
18311832
const assetConfig = composeAssetConfig(bundle, format);
18321833

18331834
const entryChunkConfig = composeEntryChunkConfig({
1835+
useLoader: advancedEsm !== true,
18341836
enabledImportMetaUrlShim: enabledShims.cjs['import.meta.url'],
18351837
contextToWatch: outBase,
18361838
});

packages/core/src/constant.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ export const DEFAULT_CONFIG_EXTENSIONS = [
1010
] as const;
1111

1212
export const SWC_HELPERS = '@swc/helpers';
13-
export const SHEBANG_PREFIX = '#!';
14-
export const SHEBANG_REGEX: RegExp = /#!.*[\s\n\r]*$/;
15-
export const REACT_DIRECTIVE_REGEX: RegExp =
16-
/^['"]use (client|server)['"](;?)[\s\n\r]*$/;
1713

1814
const DTS_EXTENSIONS: string[] = ['d.ts', 'd.mts', 'd.cts'];
1915

packages/core/src/plugins/EntryChunkPlugin.ts

Lines changed: 5 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
import { chmodSync } from 'node:fs';
21
import { createRequire } from 'node:module';
32
import {
43
type EnvironmentConfig,
54
type RsbuildPlugin,
65
type Rspack,
76
rspack,
87
} from '@rsbuild/core';
9-
import {
10-
JS_EXTENSIONS_PATTERN,
11-
REACT_DIRECTIVE_REGEX,
12-
SHEBANG_PREFIX,
13-
SHEBANG_REGEX,
14-
} from '../constant';
8+
import { JS_EXTENSIONS_PATTERN } from '../constant';
159

1610
const require = createRequire(import.meta.url);
1711

@@ -25,26 +19,9 @@ const IMPORT_META_URL_SHIM = `const __rslib_import_meta_url__ = /*#__PURE__*/ (f
2519
})();
2620
`;
2721

28-
const matchFirstLine = (source: string, regex: RegExp): string | false => {
29-
const lineBreakPos = source.match(/(\r\n|\n)/);
30-
const firstLineContent = source.slice(0, lineBreakPos?.index);
31-
const matched = regex.exec(firstLineContent);
32-
if (!matched) {
33-
return false;
34-
}
35-
36-
return matched[0];
37-
};
38-
3922
class EntryChunkPlugin {
40-
private reactDirectives: Record<string, string> = {};
41-
4223
private shimsInjectedAssets: Set<string> = new Set();
4324

44-
private shebangChmod = 0o755;
45-
private shebangEntries: Record<string, string> = {};
46-
private shebangInjectedAssets: Set<string> = new Set();
47-
4825
private enabledImportMetaUrlShim: boolean;
4926
private contextToWatch: string | null = null;
5027

@@ -72,57 +49,11 @@ class EntryChunkPlugin {
7249
});
7350

7451
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
75-
const entries: Record<string, string> = {};
76-
for (const [key, value] of compilation.entries) {
77-
const firstDep = value.dependencies[0];
78-
if (firstDep?.request) {
79-
entries[key] = firstDep.request;
80-
}
81-
}
82-
83-
for (const name in entries) {
84-
const first = entries[name];
85-
if (!first) continue;
86-
const filename = first.split('?')[0]!;
87-
const isJs = JS_EXTENSIONS_PATTERN.test(filename);
88-
if (!isJs) continue;
89-
const content = compiler.inputFileSystem!.readFileSync!(filename, {
90-
encoding: 'utf-8',
91-
});
92-
// Shebang
93-
if (content.startsWith(SHEBANG_PREFIX)) {
94-
const shebangMatch = matchFirstLine(content, SHEBANG_REGEX);
95-
if (shebangMatch) {
96-
this.shebangEntries[name] = shebangMatch;
97-
}
98-
}
99-
// React directive
100-
const reactDirective = matchFirstLine(content, REACT_DIRECTIVE_REGEX);
101-
if (reactDirective) {
102-
this.reactDirectives[name] = reactDirective;
103-
}
104-
}
105-
});
106-
107-
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
108-
compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (chunk, filename) => {
52+
compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (_chunk, filename) => {
10953
const isJs = JS_EXTENSIONS_PATTERN.test(filename);
11054
if (!isJs) return;
11155

11256
this.shimsInjectedAssets.add(filename);
113-
114-
const name = chunk.name;
115-
if (!name) return;
116-
117-
const shebangEntry = this.shebangEntries[name];
118-
if (shebangEntry) {
119-
this.shebangEntries[filename] = shebangEntry;
120-
}
121-
122-
const reactDirective = this.reactDirectives[name];
123-
if (reactDirective) {
124-
this.reactDirectives[filename] = reactDirective;
125-
}
12657
});
12758
});
12859

@@ -158,45 +89,6 @@ class EntryChunkPlugin {
15889
});
15990
}
16091
});
161-
162-
compilation.hooks.processAssets.tap(
163-
{
164-
name: PLUGIN_NAME,
165-
// Just after minify stage, to avoid from being minified.
166-
stage: rspack.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING - 1,
167-
},
168-
(assets) => {
169-
const chunkAsset = Object.keys(assets);
170-
for (const name of chunkAsset) {
171-
const shebangValue = this.shebangEntries[name];
172-
const reactDirectiveValue = this.reactDirectives[name];
173-
174-
if (shebangValue || reactDirectiveValue) {
175-
compilation.updateAsset(name, (old) => {
176-
const replaceSource = new rspack.sources.ReplaceSource(old);
177-
// Shebang
178-
if (shebangValue) {
179-
replaceSource.insert(0, `${shebangValue}\n`);
180-
this.shebangInjectedAssets.add(name);
181-
}
182-
183-
// React directives
184-
if (reactDirectiveValue) {
185-
replaceSource.insert(0, `${reactDirectiveValue}\n`);
186-
}
187-
188-
return replaceSource;
189-
});
190-
}
191-
}
192-
},
193-
);
194-
});
195-
196-
compiler.hooks.assetEmitted.tap(PLUGIN_NAME, (file, { targetPath }) => {
197-
if (this.shebangInjectedAssets.has(file)) {
198-
chmodSync(targetPath, this.shebangChmod);
199-
}
20092
});
20193
}
20294
}
@@ -217,13 +109,15 @@ const entryModuleLoaderRsbuildPlugin = (): RsbuildPlugin => ({
217109

218110
export const composeEntryChunkConfig = ({
219111
enabledImportMetaUrlShim,
112+
useLoader,
220113
contextToWatch = null,
221114
}: {
115+
useLoader: boolean;
222116
enabledImportMetaUrlShim: boolean;
223117
contextToWatch: string | null;
224118
}): EnvironmentConfig => {
225119
return {
226-
plugins: [entryModuleLoaderRsbuildPlugin()],
120+
plugins: useLoader ? [entryModuleLoaderRsbuildPlugin()] : [],
227121
tools: {
228122
rspack: {
229123
plugins: [
Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
11
import type { Rspack } from '@rsbuild/core';
2-
import { REACT_DIRECTIVE_REGEX, SHEBANG_REGEX } from '../constant';
3-
4-
function splitFromFirstLine(text: string): [string, string] {
5-
const match = text.match(/(\r\n|\n)/);
6-
if (!match) {
7-
return [text, ''];
8-
}
9-
10-
return [text.slice(0, match.index), text.slice(match.index)];
11-
}
122

3+
// Empty loader, only to make doppelganger of entry module.
134
const loader: Rspack.LoaderDefinition = function loader(source) {
14-
let result = source;
15-
16-
const [firstLine1, rest] = splitFromFirstLine(result);
17-
18-
if (SHEBANG_REGEX.test(firstLine1)) {
19-
result = rest;
20-
}
21-
22-
const [firstLine2, rest2] = splitFromFirstLine(result);
23-
if (REACT_DIRECTIVE_REGEX.test(firstLine2)) {
24-
result = rest2;
25-
}
26-
27-
return result;
5+
return source;
286
};
297

308
export default loader;

0 commit comments

Comments
 (0)