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
3,434 changes: 860 additions & 2,574 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@
"@addon-core/browser": "^0.2.1",
"@addon-core/inject-script": "^0.3.1",
"@addon-core/storage": "^0.4.0",
"@rsdoctor/rspack-plugin": "^1.3.11",
"@rspack/cli": "^1.6.7",
"@rspack/core": "^1.6.7",
"@rsdoctor/rspack-plugin": "^1.5.1",
"@rspack/cli": "^1.7.5",
"@rspack/core": "^1.7.5",
"@svgr/webpack": "^8.1.0",
"await-lock": "^3.0.0",
"c12": "^3.3.2",
Expand Down Expand Up @@ -157,6 +157,7 @@
"zod": "^3.24.2"
},
"devDependencies": {
"glob": "^13.0.1",
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
"@microsoft/api-extractor": "^7.53.1",
Expand All @@ -173,6 +174,7 @@
"@types/pluralize": "^0.0.33",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@types/semver": "^7.7.1",
"cross-env": "^7.0.3",
"dotenv": "^16.4.7",
"esbuild": "^0.25.10",
Expand Down Expand Up @@ -211,10 +213,8 @@
}
},
"overrides": {
"glob": "$glob",
"jsdom": "27.4.0",
"html-rspack-tags-plugin": {
"glob": "^10.4.1"
},
"test-exclude": "^7.0.1",
"tsup": {
"source-map": "^0.7.4"
Expand Down
143 changes: 11 additions & 132 deletions src/cli/bundler/utils/optimization.ts
Original file line number Diff line number Diff line change
@@ -1,144 +1,23 @@
import path from "path";
import {NormalModule, type OptimizationSplitChunksCacheGroupTestFn} from "@rspack/core";
import {type OptimizationSplitChunksCacheGroupTestFn} from "@rspack/core";

import {PackageName} from "@typing/app";
export const onlyViaTopLevelEntry = (entryTypes: string | string[]): OptimizationSplitChunksCacheGroupTestFn => {
const types = Array.isArray(entryTypes) ? entryTypes : [entryTypes];

const getEntryDirs = (entry: string | string[]): string[] => {
const entries = Array.isArray(entry) ? entry : [entry];
return (module, {chunkGraph}) => {
const chunks = chunkGraph.getModuleChunks(module);

if (entries.length === 0) {
return [];
}

return entries.flatMap(v => [
path.join("node_modules", PackageName, "dist", "entry", v),
path.join("addonbone", "dist", "entry", v), // TODO: Only for test. Remove this
path.join("addon-bone", "dist", "entry", v), // TODO: Only for test. Remove this
]);
};

export const onlyViaTopLevelEntry = (entry: string | string[]): OptimizationSplitChunksCacheGroupTestFn => {
const entryDirs = getEntryDirs(entry);

if (entryDirs.length === 0) {
return () => false;
}

const isTargetRes = (res?: string) => !!res && entryDirs.some(dir => res!.includes(dir));

const memoTopLevelTarget = new WeakMap<object, boolean>();
const memoPathOk = new WeakMap<object, boolean>();

const isTopLevelTarget = (m: NormalModule | undefined, mg: any): boolean => {
if (!m) {
return false;
}

const cached = memoTopLevelTarget.get(m);

if (cached !== undefined) {
return cached;
}

const ok =
isTargetRes(m.resource) &&
(() => {
const issuer = mg.getIssuer(m) as NormalModule | undefined;
return !!issuer && !mg.getIssuer(issuer);
})();

memoTopLevelTarget.set(m, ok);

return ok;
};

const pathHasTopLevelTarget = (start: NormalModule | undefined, mg: any): boolean => {
if (!start) {
return false;
}

const cached = memoPathOk.get(start);

if (cached !== undefined) {
return cached;
}

let cur: NormalModule | undefined = start;
let steps = 0;

const MAX_STEPS = 1024;

while (cur && steps++ < MAX_STEPS) {
if (isTopLevelTarget(cur, mg)) {
memoPathOk.set(start, true);
return true;
}

cur = mg.getIssuer(cur) as NormalModule | undefined;
}

memoPathOk.set(start, false);

return false;
};

return (module, {moduleGraph}) => {
const nm = module as NormalModule;
const res = nm.resource;

if (!res) {
return false;
}

if (isTargetRes(res)) return true;

const conns = moduleGraph.getIncomingConnections(nm);

if (!conns || conns.length === 0) {
if (chunks.length === 0) {
return false;
}

for (let i = 0; i < conns.length; i++) {
const origin = conns[i].originModule as NormalModule | undefined;
return chunks.every(chunk => {
const name = chunk.name;

if (!pathHasTopLevelTarget(origin, moduleGraph)) {
if (!name) {
return false;
}
}

return true;
};
};

export const isEntryModuleOrIssuer = (entry: string | string[]): OptimizationSplitChunksCacheGroupTestFn => {
const entryDirs = getEntryDirs(entry);

if (entryDirs.length === 0) {
return () => false;
}

return (module, {moduleGraph}) => {
const nm = module as NormalModule;
const resource = nm.resource || "";

if (entryDirs.some(dir => resource.includes(dir))) {
return true;
}

for (const connection of moduleGraph.getIncomingConnections(nm)) {
const origin = connection.originModule as NormalModule | undefined;

if (!origin?.resource) {
continue;
}

const originRes = origin.resource;

if (entryDirs.some(dir => originRes.includes(dir))) {
return true;
}
}

return false;
return types.some(type => name === type || name.endsWith(`.${type}`));
});
};
};
23 changes: 19 additions & 4 deletions src/cli/plugins/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Configuration as RspackConfig, DefinePlugin} from "@rspack/core";
import {Configuration as RspackConfig, DefinePlugin, NormalModule} from "@rspack/core";
import {merge as mergeConfig} from "webpack-merge";

import ContentManager from "./ContentManager";
Expand All @@ -9,6 +9,7 @@ import RelayDeclaration from "./RelayDeclaration";
import {definePlugin} from "@main/plugin";

import {EntrypointPlugin, onlyViaTopLevelEntry} from "@cli/bundler";
import {getResolvePath, getSourcePath} from "@cli/resolvers/path";

import {Command} from "@typing/app";
import {RelayMethod, RelayOptions} from "@typing/relay";
Expand Down Expand Up @@ -62,21 +63,35 @@ export default definePlugin(() => {
});
}

const entryTypeFilter = onlyViaTopLevelEntry(["content", "relay"]);

rspack = {
plugins: [plugin],
optimization: {
splitChunks: {
cacheGroups: {
frameworkContent: {
adnbnContent: {
minChunks: 2,
name: manager.chunkName(),
test: onlyViaTopLevelEntry(["content", "relay"]),
test: (module, context) => {
const nm = module as NormalModule;

if (!nm.resource) {
return false;
}

if (nm.resource.startsWith(getResolvePath(getSourcePath(config)))) {
return false;
}

return entryTypeFilter(module, context);
},
chunks: (chunk): boolean => {
return manager.likely(chunk.name);
},
enforce: false,
reuseExistingChunk: true,
priority: 10,
priority: 20,
},
},
},
Expand Down
67 changes: 55 additions & 12 deletions src/cli/plugins/optimization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {merge as mergeConfig} from "webpack-merge";

import {definePlugin} from "@main/plugin";

import {getResolvePath, getSourcePath} from "@cli/resolvers/path";
import {resolveRootPath} from "@cli/resolvers/path";

export default definePlugin(() => {
return {
Expand All @@ -15,42 +15,85 @@ export default definePlugin(() => {
moduleIds: "deterministic",
chunkIds: "deterministic",
mangleExports: "deterministic",
usedExports: true,
providedExports: true,
},
};

if (!config.commonChunks) {
const {commonChunks} = config;

if (!commonChunks) {
return rspack;
}

return mergeConfig(rspack, {
optimization: {
usedExports: true,
providedExports: true,
splitChunks: {
chunks: "all",
minSize: 20000,
cacheGroups: {
default: false,
defaultVendors: false,
common: {
adnbnCommon: {
test: module => {
const {resource} = module as NormalModule;

if (!resource) {
return false;
}

return resource.startsWith(getResolvePath(getSourcePath(config)));
if (/[\\/]node_modules[\\/]/.test(resource)) {
return true;
}

if (resource.startsWith(resolveRootPath(config))) {
return true;
}

const isFileSystemModule = resource.includes("/") || resource.includes("\\");
const isVirtual = resource.startsWith("virtual:") || resource.startsWith("\0");

return isFileSystemModule && !isVirtual;
},
name: (module, chunks, cacheGroupKey) => {
const entryNames = Array.from(
new Set(chunks.map(({name}) => name).filter(name => _.isString(name)))
).sort();
name: (_module, chunks) => {
const names = new Set(
chunks
.map(({name}) => name)
.filter((name): name is string => _.isString(name) && !_.isEmpty(name))
);

if (_.isFunction(commonChunks)) {
const name = commonChunks(names);

if (_.isString(name) && !_.isEmpty(name)) {
return name;
}
}

if (names.size === 0) {
return `async.common`;
}

const sortedNames = Array.from(names).toSorted();
const joinedNames = sortedNames.join("-");

if (joinedNames.length <= 60 && sortedNames.length <= 3) {
return `${joinedNames}.common`;
}

let hash = 0;

for (let i = 0; i < joinedNames.length; i++) {
hash = (hash << 5) - hash + joinedNames.charCodeAt(i);
hash |= 0;
}

const hashStr = Math.abs(hash).toString(36).slice(0, 8);
const prefix = sortedNames.slice(0, 2).join("-");

return `${entryNames.join("-")}.${cacheGroupKey}`;
return `${prefix}-etc-${hashStr}.common`;
},
minChunks: 2,
enforce: true,
priority: -10,
reuseExistingChunk: true,
},
Expand Down
Loading