Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ export function composeMinifyConfig(config: LibConfig): EnvironmentConfig {
// MF assets are loaded over the network, which means they will not be compressed by the project. Therefore, minifying them is necessary.
minify: format === 'mf',
compress: {
directives: false,
defaults: false,
unused: true,
dead_code: true,
Expand Down Expand Up @@ -1831,6 +1832,7 @@ async function composeLibRsbuildConfig(
const assetConfig = composeAssetConfig(bundle, format);

const entryChunkConfig = composeEntryChunkConfig({
useLoader: advancedEsm !== true,
enabledImportMetaUrlShim: enabledShims.cjs['import.meta.url'],
contextToWatch: outBase,
});
Expand Down
4 changes: 0 additions & 4 deletions packages/core/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ export const DEFAULT_CONFIG_EXTENSIONS = [
] as const;

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

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

Expand Down
116 changes: 5 additions & 111 deletions packages/core/src/plugins/EntryChunkPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { chmodSync } from 'node:fs';
import { createRequire } from 'node:module';
import {
type EnvironmentConfig,
type RsbuildPlugin,
type Rspack,
rspack,
} from '@rsbuild/core';
import {
JS_EXTENSIONS_PATTERN,
REACT_DIRECTIVE_REGEX,
SHEBANG_PREFIX,
SHEBANG_REGEX,
} from '../constant';
import { JS_EXTENSIONS_PATTERN } from '../constant';

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

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

const matchFirstLine = (source: string, regex: RegExp): string | false => {
const lineBreakPos = source.match(/(\r\n|\n)/);
const firstLineContent = source.slice(0, lineBreakPos?.index);
const matched = regex.exec(firstLineContent);
if (!matched) {
return false;
}

return matched[0];
};

class EntryChunkPlugin {
private reactDirectives: Record<string, string> = {};

private shimsInjectedAssets: Set<string> = new Set();

private shebangChmod = 0o755;
private shebangEntries: Record<string, string> = {};
private shebangInjectedAssets: Set<string> = new Set();

private enabledImportMetaUrlShim: boolean;
private contextToWatch: string | null = null;

Expand Down Expand Up @@ -72,57 +49,11 @@ class EntryChunkPlugin {
});

compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
const entries: Record<string, string> = {};
for (const [key, value] of compilation.entries) {
const firstDep = value.dependencies[0];
if (firstDep?.request) {
entries[key] = firstDep.request;
}
}

for (const name in entries) {
const first = entries[name];
if (!first) continue;
const filename = first.split('?')[0]!;
const isJs = JS_EXTENSIONS_PATTERN.test(filename);
if (!isJs) continue;
const content = compiler.inputFileSystem!.readFileSync!(filename, {
encoding: 'utf-8',
});
// Shebang
if (content.startsWith(SHEBANG_PREFIX)) {
const shebangMatch = matchFirstLine(content, SHEBANG_REGEX);
if (shebangMatch) {
this.shebangEntries[name] = shebangMatch;
}
}
// React directive
const reactDirective = matchFirstLine(content, REACT_DIRECTIVE_REGEX);
if (reactDirective) {
this.reactDirectives[name] = reactDirective;
}
}
});

compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (chunk, filename) => {
compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (_chunk, filename) => {
const isJs = JS_EXTENSIONS_PATTERN.test(filename);
if (!isJs) return;

this.shimsInjectedAssets.add(filename);

const name = chunk.name;
if (!name) return;

const shebangEntry = this.shebangEntries[name];
if (shebangEntry) {
this.shebangEntries[filename] = shebangEntry;
}

const reactDirective = this.reactDirectives[name];
if (reactDirective) {
this.reactDirectives[filename] = reactDirective;
}
});
});

Expand Down Expand Up @@ -158,45 +89,6 @@ class EntryChunkPlugin {
});
}
});

compilation.hooks.processAssets.tap(
{
name: PLUGIN_NAME,
// Just after minify stage, to avoid from being minified.
stage: rspack.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING - 1,
},
(assets) => {
const chunkAsset = Object.keys(assets);
for (const name of chunkAsset) {
const shebangValue = this.shebangEntries[name];
const reactDirectiveValue = this.reactDirectives[name];

if (shebangValue || reactDirectiveValue) {
compilation.updateAsset(name, (old) => {
const replaceSource = new rspack.sources.ReplaceSource(old);
// Shebang
if (shebangValue) {
replaceSource.insert(0, `${shebangValue}\n`);
this.shebangInjectedAssets.add(name);
}

// React directives
if (reactDirectiveValue) {
replaceSource.insert(0, `${reactDirectiveValue}\n`);
}

return replaceSource;
});
}
}
},
);
});

compiler.hooks.assetEmitted.tap(PLUGIN_NAME, (file, { targetPath }) => {
if (this.shebangInjectedAssets.has(file)) {
chmodSync(targetPath, this.shebangChmod);
}
});
}
}
Expand All @@ -217,13 +109,15 @@ const entryModuleLoaderRsbuildPlugin = (): RsbuildPlugin => ({

export const composeEntryChunkConfig = ({
enabledImportMetaUrlShim,
useLoader,
contextToWatch = null,
}: {
useLoader: boolean;
enabledImportMetaUrlShim: boolean;
contextToWatch: string | null;
}): EnvironmentConfig => {
return {
plugins: [entryModuleLoaderRsbuildPlugin()],
plugins: useLoader ? [entryModuleLoaderRsbuildPlugin()] : [],
tools: {
rspack: {
plugins: [
Expand Down
26 changes: 2 additions & 24 deletions packages/core/src/plugins/entryModuleLoader.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,8 @@
import type { Rspack } from '@rsbuild/core';
import { REACT_DIRECTIVE_REGEX, SHEBANG_REGEX } from '../constant';

function splitFromFirstLine(text: string): [string, string] {
const match = text.match(/(\r\n|\n)/);
if (!match) {
return [text, ''];
}

return [text.slice(0, match.index), text.slice(match.index)];
}

// Empty loader, only to make doppelganger of entry module.
const loader: Rspack.LoaderDefinition = function loader(source) {
let result = source;

const [firstLine1, rest] = splitFromFirstLine(result);

if (SHEBANG_REGEX.test(firstLine1)) {
result = rest;
}

const [firstLine2, rest2] = splitFromFirstLine(result);
if (REACT_DIRECTIVE_REGEX.test(firstLine2)) {
result = rest2;
}

return result;
return source;
};

export default loader;
Loading
Loading