Skip to content

Commit

Permalink
fix: handle add and unlink file in bundleless mode
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Jan 6, 2025
1 parent 3564bcb commit 3c6d306
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 94 deletions.
129 changes: 72 additions & 57 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -918,76 +918,90 @@ const composeEntryConfig = async (
};
}

// In bundleless mode, resolve glob patterns and convert them to entry object.
const resolvedEntries: Record<string, string> = {};
for (const key of Object.keys(entries)) {
const entry = entries[key];

// Entries in bundleless mode could be:
// 1. A string of glob pattern: { entry: { index: 'src/*.ts' } }
// 2. An array of glob patterns: { entry: { index: ['src/*.ts', 'src/*.tsx'] } }
// Not supported for now: entry description object
const entryFiles = Array.isArray(entry)
? entry
: typeof entry === 'string'
? [entry]
: null;

if (!entryFiles) {
throw new Error(
'Entry can only be a string or an array of strings for now',
);
}
const globScanEntries = async (calcLcp: boolean) => {
// In bundleless mode, resolve glob patterns and convert them to entry object.
const resolvedEntries: Record<string, string> = {};
for (const key of Object.keys(entries)) {
const entry = entries[key];

// Entries in bundleless mode could be:
// 1. A string of glob pattern: { entry: { index: 'src/*.ts' } }
// 2. An array of glob patterns: { entry: { index: ['src/*.ts', 'src/*.tsx'] } }
// Not supported for now: entry description object
const entryFiles = Array.isArray(entry)
? entry
: typeof entry === 'string'
? [entry]
: null;

// Turn entries in array into each separate entry.
const globEntryFiles = await glob(entryFiles, {
cwd: root,
absolute: true,
});
if (!entryFiles) {
throw new Error(
'Entry can only be a string or an array of strings for now',
);
}

// Filter the glob resolved entry files based on the allowed extensions
const resolvedEntryFiles = globEntryFiles.filter((file) =>
ENTRY_EXTENSIONS_PATTERN.test(file),
);
// Turn entries in array into each separate entry.
const globEntryFiles = await glob(entryFiles, {
cwd: root,
absolute: true,
});

if (resolvedEntryFiles.length === 0) {
throw new Error(`Cannot find ${resolvedEntryFiles}`);
}
// Filter the glob resolved entry files based on the allowed extensions
const resolvedEntryFiles = globEntryFiles.filter((file) =>
ENTRY_EXTENSIONS_PATTERN.test(file),
);

if (resolvedEntryFiles.length === 0) {
throw new Error(`Cannot find ${resolvedEntryFiles}`);
}

// Similar to `rootDir` in tsconfig and `outbase` in esbuild.
const lcp = await calcLongestCommonPath(resolvedEntryFiles);
// Using the longest common path of all non-declaration input files by default.
const outBase = lcp === null ? root : lcp;

// Similar to `rootDir` in tsconfig and `outbase` in esbuild.
const lcp = await calcLongestCommonPath(resolvedEntryFiles);
// Using the longest common path of all non-declaration input files by default.
const outBase = lcp === null ? root : lcp;
function getEntryName(file: string) {
const { dir, name } = path.parse(path.relative(outBase, file));
// Entry filename contains nested path to preserve source directory structure.
const entryFileName = path.join(dir, name);

function getEntryName(file: string) {
const { dir, name } = path.parse(path.relative(outBase, file));
// Entry filename contains nested path to preserve source directory structure.
const entryFileName = path.join(dir, name);
// 1. we mark the global css files (which will generate empty js chunk in cssExtract), and deleteAsset in RemoveCssExtractAssetPlugin
// 2. avoid the same name e.g: `index.ts` and `index.css`
if (isCssGlobalFile(file, cssModulesAuto)) {
return `${RSLIB_CSS_ENTRY_FLAG}/${entryFileName}`;
}

// 1. we mark the global css files (which will generate empty js chunk in cssExtract), and deleteAsset in RemoveCssExtractAssetPlugin
// 2. avoid the same name e.g: `index.ts` and `index.css`
if (isCssGlobalFile(file, cssModulesAuto)) {
return `${RSLIB_CSS_ENTRY_FLAG}/${entryFileName}`;
return entryFileName;
}

return entryFileName;
for (const file of resolvedEntryFiles) {
const entryName = getEntryName(file);
if (resolvedEntries[entryName]) {
logger.warn(
`duplicate entry: ${entryName}, this may lead to the incorrect output, please rename the file`,
);
}
resolvedEntries[entryName] = file;
}
}

for (const file of resolvedEntryFiles) {
const entryName = getEntryName(file);
if (resolvedEntries[entryName]) {
logger.warn(
`duplicate entry: ${entryName}, this may lead to the incorrect output, please rename the file`,
);
}
resolvedEntries[entryName] = file;
if (calcLcp) {
const lcp = await calcLongestCommonPath(Object.values(resolvedEntries));
return { resolvedEntries, lcp };
}
}
return { resolvedEntries, lcp: null };
};

const lcp = await calcLongestCommonPath(Object.values(resolvedEntries));
// LCP could only be determined at the first time of glob scan.
const { lcp } = await globScanEntries(true);
const entryConfig: EnvironmentConfig = {
source: {
entry: appendEntryQuery(resolvedEntries),
tools: {
rspack: {
entry: async () => {
const { resolvedEntries } = await globScanEntries(false);
return appendEntryQuery(resolvedEntries);
},
},
},
};

Expand Down Expand Up @@ -1342,6 +1356,7 @@ async function composeLibRsbuildConfig(

const entryChunkConfig = composeEntryChunkConfig({
enabledImportMetaUrlShim: enabledShims.cjs['import.meta.url'],
contextToWatch: lcp,
});
const dtsConfig = await composeDtsConfig(config, dtsExtension);
const externalsWarnConfig = composeExternalsWarnConfig(
Expand Down
63 changes: 45 additions & 18 deletions packages/core/src/plugins/EntryChunkPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,48 +37,54 @@ class EntryChunkPlugin {
private shebangInjectedAssets: Set<string> = new Set();

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

constructor({
enabledImportMetaUrlShim = true,
contextToWatch,
}: {
enabledImportMetaUrlShim: boolean;
contextToWatch: string | null;
}) {
this.enabledImportMetaUrlShim = enabledImportMetaUrlShim;
this.contextToWatch = contextToWatch;
}

apply(compiler: Rspack.Compiler) {
compiler.hooks.entryOption.tap(PLUGIN_NAME, (_context, entries) => {
for (const name in entries) {
const entry = (entries as Rspack.EntryStaticNormalized)[name];
if (!entry) continue;

let first: string | undefined;
if (Array.isArray(entry)) {
first = entry[0];
} else if (Array.isArray(entry.import)) {
first = entry.import[0];
} else if (typeof entry === 'string') {
first = entry;
}
compiler.hooks.afterCompile.tap(PLUGIN_NAME, (compilation) => {
if (this.contextWatched || this.contextToWatch === null) return;

const contextDep = compilation.contextDependencies;
contextDep.add(this.contextToWatch);
this.contextWatched = true;
});

if (typeof first !== 'string') continue;
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) {
Expand All @@ -87,7 +93,25 @@ class EntryChunkPlugin {
}
});

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

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;
}
});

compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (chunk, filename) => {
const isJs = JS_EXTENSIONS_PATTERN.test(filename);
if (!isJs) return;
Expand Down Expand Up @@ -192,8 +216,10 @@ const entryModuleLoaderRsbuildPlugin = (): RsbuildPlugin => ({

export const composeEntryChunkConfig = ({
enabledImportMetaUrlShim,
contextToWatch = null,
}: {
enabledImportMetaUrlShim: boolean;
contextToWatch: string | null;
}): EnvironmentConfig => {
return {
plugins: [entryModuleLoaderRsbuildPlugin()],
Expand All @@ -202,6 +228,7 @@ export const composeEntryChunkConfig = ({
plugins: [
new EntryChunkPlugin({
enabledImportMetaUrlShim,
contextToWatch,
}),
],
},
Expand Down
8 changes: 8 additions & 0 deletions packages/core/tests/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
{
"plugins": [
EntryChunkPlugin {
"contextToWatch": null,
"contextWatched": false,
"enabledImportMetaUrlShim": false,
"reactDirectives": {},
"shebangChmod": 493,
Expand Down Expand Up @@ -449,6 +451,8 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
{
"plugins": [
EntryChunkPlugin {
"contextToWatch": null,
"contextWatched": false,
"enabledImportMetaUrlShim": true,
"reactDirectives": {},
"shebangChmod": 493,
Expand Down Expand Up @@ -668,6 +672,8 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
{
"plugins": [
EntryChunkPlugin {
"contextToWatch": null,
"contextWatched": false,
"enabledImportMetaUrlShim": false,
"reactDirectives": {},
"shebangChmod": 493,
Expand Down Expand Up @@ -822,6 +828,8 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
{
"plugins": [
EntryChunkPlugin {
"contextToWatch": null,
"contextWatched": false,
"enabledImportMetaUrlShim": false,
"reactDirectives": {},
"shebangChmod": 493,
Expand Down
Loading

0 comments on commit 3c6d306

Please sign in to comment.