import fs from "node:fs";
|
import path from "node:path";
|
import {BuildEnvironmentOptions} from "vite";
|
|
// The @dh-software/* packages are pnpm-linked. Some link straight into the local
|
// node_modules/.pnpm store (their realpath still contains "@dh-software"), but the
|
// dev-linked ones (furnview-components, webui-api, ...) resolve through the global
|
// store down to source folders like C:/furnview_dev/furnview-components — a realpath
|
// that does NOT contain "@dh-software". Matching by realpath root catches both.
|
const dhScopeDir = path.join(process.cwd(), "node_modules", "@dh-software");
|
|
const wuiRoots: string[] = []; // @dh-software/webui-*
|
const dhRoots: string[] = []; // the rest of @dh-software (components + icons)
|
for (const name of fs.readdirSync(dhScopeDir)) {
|
if (name === "vite") continue; // build-time only, never in the client bundle
|
try {
|
const root = fs.realpathSync(path.join(dhScopeDir, name)).replace(/\\/g, "/");
|
(name.startsWith("webui") ? wuiRoots : dhRoots).push(root);
|
} catch {
|
// broken/dangling link — skip
|
}
|
}
|
|
const norm = (id: string): string => id.replace(/\\/g, "/");
|
|
// A module belongs to one of these packages only if it's the package's OWN code
|
// (under its root, but not inside a nested node_modules). Nested deps such as lit
|
// or @dh-software/lit-extensions therefore fall through to the "vendor" group —
|
// which is what keeps the chunk graph acyclic: if a shared lib like lit-extensions
|
// stayed in "dh", then webui-* (wui) importing it plus furnview-components (dh)
|
// importing webui-* would form a circular dh <-> wui chunk dependency.
|
const ownModuleOf = (id: string, roots: string[]): boolean => {
|
const n = norm(id);
|
return roots.some(root => n.startsWith(root + "/") && !n.slice(root.length).includes("/node_modules/"));
|
};
|
|
const isWui = (id: string): boolean => ownModuleOf(id, wuiRoots);
|
const isDh = (id: string): boolean => ownModuleOf(id, dhRoots);
|
|
interface CodeSplittingGroup {
|
name: string;
|
priority?: number;
|
test: (id: string) => boolean;
|
}
|
|
const groups: CodeSplittingGroup[] = [
|
// First-party @dh-software code wins over the generic "vendor" catch-all.
|
// webui-* -> wui, the rest (components, icons) -> dh.
|
{name: "dh", priority: 2, test: isDh},
|
{name: "wui", priority: 2, test: isWui},
|
// Everything else in node_modules (lit, lit-extensions, xlsx, ...).
|
{name: "vendor", priority: 1, test: (id) => norm(id).includes("/node_modules/")},
|
];
|
|
const vendorNames = new Set(groups.map(g => g.name));
|
|
export const rolldownOptions: BuildEnvironmentOptions["rolldownOptions"] = {
|
output: {
|
// Vendor chunks go under dist/vendor/<name>.chunk.js; anything else keeps
|
// the standard fv.[hash].js entry-style naming at the dist root.
|
chunkFileNames: (chunk) =>
|
vendorNames.has(chunk.name) ? `vendor/${chunk.name}.chunk.js` : "fv.[hash].js",
|
codeSplitting: {
|
// Without this, every group also drags in its captured modules' full
|
// dependency tree, so the "dh" group would swallow lit before the
|
// "vendor" group could claim it.
|
includeDependenciesRecursively: false,
|
groups,
|
},
|
},
|
};
|