mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
Optimize Windows packaged size pruning
This commit is contained in:
@@ -317,6 +317,50 @@ async function pruneBrokenSymlinks(root, current = root, removedPaths = [], reas
|
||||
return removedPaths;
|
||||
}
|
||||
|
||||
function isSourceBuildResidue(relativePath) {
|
||||
const normalized = relativePath.split(path.sep).join("/");
|
||||
return normalized.endsWith(".map") || normalized.endsWith(".tsbuildinfo");
|
||||
}
|
||||
|
||||
async function pruneMatchingFilesSummary(root, includeRelativePath, reason) {
|
||||
const summary = {
|
||||
bytes: 0,
|
||||
count: 0,
|
||||
reason,
|
||||
root,
|
||||
};
|
||||
|
||||
async function visit(current) {
|
||||
let metadata;
|
||||
try {
|
||||
metadata = await lstat(current);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (metadata.isDirectory()) {
|
||||
const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
|
||||
for (const entry of entries) {
|
||||
await visit(path.join(current, entry.name));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const relativePath = path.relative(root, current);
|
||||
if (relativePath.length === 0 || !includeRelativePath(relativePath)) return;
|
||||
summary.bytes += metadata.size;
|
||||
summary.count += 1;
|
||||
await rm(current, { force: true });
|
||||
}
|
||||
|
||||
await visit(root);
|
||||
return summary.count > 0 ? [summary] : [];
|
||||
}
|
||||
|
||||
async function pruneSourceBuildResidue(root, reason) {
|
||||
return await pruneMatchingFilesSummary(root, isSourceBuildResidue, reason);
|
||||
}
|
||||
|
||||
function isForbiddenCopiedEntry(relativePath, platformName) {
|
||||
const normalized = relativePath.split(path.sep).join("/");
|
||||
const withRootSlash = `/${normalized}`;
|
||||
@@ -378,6 +422,7 @@ async function auditCopiedStandalone(config, installResult, platformName) {
|
||||
const staticRoot = path.join(installResult.destinationWebRoot, ".next", "static");
|
||||
const publicRoot = path.join(installResult.destinationWebRoot, "public");
|
||||
const nodeModulesRoot = path.join(installResult.destinationRoot, "node_modules");
|
||||
const webNodeModulesRoot = path.join(installResult.destinationWebRoot, "node_modules");
|
||||
const requiredPaths = [serverPath, staticRoot, nodeModulesRoot];
|
||||
if (await pathExists(config.webPublicSourceRoot)) requiredPaths.push(publicRoot);
|
||||
|
||||
@@ -413,6 +458,8 @@ async function auditCopiedStandalone(config, installResult, platformName) {
|
||||
resolvedModules,
|
||||
serverPath,
|
||||
symlinks: closureStats.symlinks,
|
||||
webNextBytes: await sizePathBytes(path.join(webNodeModulesRoot, "next")),
|
||||
webNodeModulesBytes: await sizePathBytes(webNodeModulesRoot),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -458,6 +505,39 @@ async function pruneRootSharp(appNodeModulesRoot) {
|
||||
return removedPaths;
|
||||
}
|
||||
|
||||
async function pruneRootWebPackage(appNodeModulesRoot, platformName) {
|
||||
if (platformName !== "win32") return [];
|
||||
|
||||
const webPackageRoot = path.join(appNodeModulesRoot, "@open-design", "web");
|
||||
const removedPaths = [];
|
||||
for (const entry of [".next", "app", "next.config.ts", "public", "src"]) {
|
||||
await removePathAndRecord(
|
||||
path.join(webPackageRoot, entry),
|
||||
"root @open-design/web standalone-safe package residue",
|
||||
removedPaths,
|
||||
);
|
||||
}
|
||||
return removedPaths;
|
||||
}
|
||||
|
||||
async function auditRootWebPackage(appNodeModulesRoot) {
|
||||
const webPackageRoot = path.join(appNodeModulesRoot, "@open-design", "web");
|
||||
const packageJsonPath = path.join(webPackageRoot, "package.json");
|
||||
const sidecarEntryPath = path.join(webPackageRoot, "dist", "sidecar", "index.js");
|
||||
for (const requiredPath of [packageJsonPath, sidecarEntryPath]) {
|
||||
if (!(await pathExists(requiredPath))) {
|
||||
throw new Error(`[tools-pack web-standalone] root @open-design/web audit missing: ${requiredPath}`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
bytes: await sizePathBytes(webPackageRoot),
|
||||
packageJsonPath,
|
||||
retainedDistBytes: await sizePathBytes(path.join(webPackageRoot, "dist")),
|
||||
sidecarEntryPath,
|
||||
webPackageRoot,
|
||||
};
|
||||
}
|
||||
|
||||
async function auditNoBrokenSymlinks(root, label) {
|
||||
const stats = await collectClosureStats(root);
|
||||
if (stats.brokenSymlinks.length > 0) {
|
||||
@@ -485,6 +565,9 @@ async function runWebStandaloneAfterPack(context) {
|
||||
const appNodeModulesRoot = resolveRootAppNodeModulesRoot(resourcesRoot);
|
||||
const installResult = await installStandaloneResource(config, resourcesRoot, context.electronPlatformName);
|
||||
const copiedPrune = config.pruneCopiedSharp ? await pruneCopiedSharp(installResult.destinationRoot) : [];
|
||||
const copiedBuildResiduePrune = context.electronPlatformName === "win32"
|
||||
? await pruneSourceBuildResidue(installResult.destinationRoot, "copied standalone source/build residue")
|
||||
: [];
|
||||
const brokenSymlinkPrune = await pruneBrokenSymlinks(
|
||||
installResult.destinationRoot,
|
||||
installResult.destinationRoot,
|
||||
@@ -494,6 +577,13 @@ async function runWebStandaloneAfterPack(context) {
|
||||
const copiedAudit = await auditCopiedStandalone(config, installResult, context.electronPlatformName);
|
||||
const rootPrune = config.pruneRootNext ? await pruneRootNext(appNodeModulesRoot, context.electronPlatformName) : [];
|
||||
const rootSharpPrune = config.pruneRootSharp ? await pruneRootSharp(appNodeModulesRoot) : [];
|
||||
const rootWebPackagePrune = await pruneRootWebPackage(appNodeModulesRoot, context.electronPlatformName);
|
||||
const rootBuildResiduePrune = context.electronPlatformName === "win32"
|
||||
? await pruneSourceBuildResidue(appNodeModulesRoot, "root app source/build residue")
|
||||
: [];
|
||||
const rootWebPackageAudit = context.electronPlatformName === "win32"
|
||||
? await auditRootWebPackage(appNodeModulesRoot)
|
||||
: null;
|
||||
const rootBrokenSymlinkPrune = await pruneBrokenSymlinks(
|
||||
appNodeModulesRoot,
|
||||
appNodeModulesRoot,
|
||||
@@ -508,14 +598,18 @@ async function runWebStandaloneAfterPack(context) {
|
||||
appPath,
|
||||
brokenSymlinkPrune,
|
||||
copiedAudit,
|
||||
copiedBuildResiduePrune,
|
||||
copiedPrune,
|
||||
generatedAt: new Date().toISOString(),
|
||||
platformName: context.electronPlatformName,
|
||||
resourcesRoot,
|
||||
rootBuildResiduePrune,
|
||||
rootBrokenSymlinkPrune,
|
||||
rootPrune,
|
||||
rootSharpPrune,
|
||||
rootSymlinkAudit,
|
||||
rootWebPackageAudit,
|
||||
rootWebPackagePrune,
|
||||
sourceWebRoot: installResult.sourceWebRoot,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
+107
-7
@@ -37,6 +37,20 @@ const PRODUCT_NAME = "Open Design";
|
||||
const DESKTOP_LOG_ECHO_ENV = "OD_DESKTOP_LOG_ECHO";
|
||||
const WEB_STANDALONE_HOOK_CONFIG_ENV = "OD_TOOLS_PACK_WEB_STANDALONE_HOOK_CONFIG";
|
||||
const WEB_STANDALONE_RESOURCE_NAME = "open-design-web-standalone";
|
||||
const ELECTRON_BUILDER_ASAR = false;
|
||||
const ELECTRON_BUILDER_FILE_PATTERNS = [
|
||||
"**/*",
|
||||
"!**/node_modules/.bin",
|
||||
"!**/node_modules/electron{,/**/*}",
|
||||
"!**/*.map",
|
||||
"!**/*.tsbuildinfo",
|
||||
"!**/.next/cache",
|
||||
"!**/.next/cache/**",
|
||||
"!**/node_modules/better-sqlite3/build/Release/obj",
|
||||
"!**/node_modules/better-sqlite3/build/Release/obj/**",
|
||||
"!**/node_modules/better-sqlite3/deps",
|
||||
"!**/node_modules/better-sqlite3/deps/**",
|
||||
] as const;
|
||||
const NSIS_INSTALLER_LANGUAGE_BY_WEB_LOCALE = {
|
||||
en: "en_US",
|
||||
fa: "fa_IR",
|
||||
@@ -110,6 +124,7 @@ export type WinPackResult = {
|
||||
export type WinSizeReport = {
|
||||
builder: {
|
||||
asar: boolean;
|
||||
filePatterns: readonly string[];
|
||||
targets: Array<"dir" | "nsis">;
|
||||
webOutputMode: ToolPackConfig["webOutputMode"];
|
||||
};
|
||||
@@ -118,14 +133,39 @@ export type WinSizeReport = {
|
||||
outputRootBytes: number;
|
||||
resourceRootBytes: number;
|
||||
runtimeNamespaceRoot: string;
|
||||
topLevel: {
|
||||
appResourcesBytes: number;
|
||||
copiedStandaloneBytes: number;
|
||||
electronLocalesBytes: number;
|
||||
resourcesBytes: number;
|
||||
};
|
||||
tracked: {
|
||||
appNodeModulesBytes: number;
|
||||
betterSqlite3Bytes: number;
|
||||
betterSqlite3SourceResidueBytes: number;
|
||||
bundledNodeBytes: number;
|
||||
copiedStandaloneNextBytes: number;
|
||||
copiedStandaloneNextSwcBytes: number;
|
||||
copiedStandaloneNodeModulesBytes: number;
|
||||
copiedStandaloneSharpLibvipsBytes: number;
|
||||
copiedStandaloneSourcemapBytes: number;
|
||||
copiedStandaloneTsbuildInfoBytes: number;
|
||||
copiedStandaloneWebNextBytes: number;
|
||||
copiedStandaloneWebNodeModulesBytes: number;
|
||||
electronLocalesBytes: number;
|
||||
markdownBytes: number;
|
||||
nextBytes: number;
|
||||
nextSwcBytes: number;
|
||||
sharpLibvipsBytes: number;
|
||||
sourcemapBytes: number;
|
||||
tsbuildInfoBytes: number;
|
||||
webCopiedStandaloneBytes: number;
|
||||
webNextCacheBytes: number;
|
||||
webPackageAppBytes: number;
|
||||
webPackageBytes: number;
|
||||
webPackageDistBytes: number;
|
||||
webPackagePublicBytes: number;
|
||||
webPackageSrcBytes: number;
|
||||
webPackageStandaloneBytes: number;
|
||||
};
|
||||
unpackedBytes: number | null;
|
||||
@@ -449,6 +489,23 @@ async function sizeExistingFileBytes(path: string): Promise<number | null> {
|
||||
return metadata == null ? null : metadata.size;
|
||||
}
|
||||
|
||||
async function sumChildDirectorySizes(path: string, includeChild: (name: string) => boolean): Promise<number> {
|
||||
const entries = await readdir(path, { withFileTypes: true }).catch(() => []);
|
||||
let total = 0;
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || !includeChild(entry.name)) continue;
|
||||
total += await sizePathBytes(join(path, entry.name));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
function isBetterSqlite3SourceResidue(path: string): boolean {
|
||||
return (
|
||||
path.includes("/node_modules/better-sqlite3/deps/") ||
|
||||
path.includes("/node_modules/better-sqlite3/build/Release/obj/")
|
||||
);
|
||||
}
|
||||
|
||||
async function listChildDirectories(root: string): Promise<string[]> {
|
||||
try {
|
||||
const entries = await readdir(root, { withFileTypes: true });
|
||||
@@ -835,7 +892,7 @@ async function runElectronBuilder(config: ToolPackConfig, paths: WinPaths): Prom
|
||||
const builderConfig = {
|
||||
appId: "io.open-design.desktop",
|
||||
afterPack: webStandaloneHookConfigPath == null ? undefined : winResources.webStandaloneAfterPackHook,
|
||||
asar: false,
|
||||
asar: ELECTRON_BUILDER_ASAR,
|
||||
buildDependenciesFromSource: false,
|
||||
compression: "maximum",
|
||||
directories: { output: paths.appBuilderOutputRoot },
|
||||
@@ -852,7 +909,7 @@ async function runElectronBuilder(config: ToolPackConfig, paths: WinPaths): Prom
|
||||
{ from: paths.resourceRoot, to: "open-design" },
|
||||
{ from: paths.packagedConfigPath, to: "open-design-config.json" },
|
||||
],
|
||||
files: ["**/*", "!**/node_modules/.bin", "!**/node_modules/electron{,/**/*}"],
|
||||
files: [...ELECTRON_BUILDER_FILE_PATTERNS],
|
||||
forceCodeSigning: false,
|
||||
icon: paths.winIconPath,
|
||||
nodeGypRebuild: false,
|
||||
@@ -946,9 +1003,15 @@ async function writeLocalLatestYml(config: ToolPackConfig, paths: WinPaths): Pro
|
||||
async function collectWinSizeReport(config: ToolPackConfig, paths: WinPaths): Promise<WinSizeReport> {
|
||||
const appResourcesRoot = join(paths.unpackedRoot, "resources");
|
||||
const appNodeModulesRoot = join(appResourcesRoot, "app", "node_modules");
|
||||
const copiedStandaloneRoot = join(appResourcesRoot, WEB_STANDALONE_RESOURCE_NAME);
|
||||
const copiedStandaloneNodeModulesRoot = join(copiedStandaloneRoot, "node_modules");
|
||||
const copiedStandaloneWebNodeModulesRoot = join(copiedStandaloneRoot, "apps", "web", "node_modules");
|
||||
const electronLocalesRoot = join(paths.unpackedRoot, "locales");
|
||||
const rootWebPackageRoot = join(appNodeModulesRoot, "@open-design", "web");
|
||||
return {
|
||||
builder: {
|
||||
asar: false,
|
||||
asar: ELECTRON_BUILDER_ASAR,
|
||||
filePatterns: ELECTRON_BUILDER_FILE_PATTERNS,
|
||||
targets: resolveWinTargets(config.to),
|
||||
webOutputMode: config.webOutputMode,
|
||||
},
|
||||
@@ -957,15 +1020,52 @@ async function collectWinSizeReport(config: ToolPackConfig, paths: WinPaths): Pr
|
||||
outputRootBytes: await sizePathBytes(config.roots.output.namespaceRoot),
|
||||
resourceRootBytes: await sizePathBytes(paths.resourceRoot),
|
||||
runtimeNamespaceRoot: config.roots.runtime.namespaceRoot,
|
||||
topLevel: {
|
||||
appResourcesBytes: await sizePathBytes(join(appResourcesRoot, "app")),
|
||||
copiedStandaloneBytes: await sizePathBytes(copiedStandaloneRoot),
|
||||
electronLocalesBytes: await sizePathBytes(electronLocalesRoot),
|
||||
resourcesBytes: await sizePathBytes(appResourcesRoot),
|
||||
},
|
||||
tracked: {
|
||||
appNodeModulesBytes: await sizePathBytes(appNodeModulesRoot),
|
||||
betterSqlite3Bytes: await sizePathBytes(join(appNodeModulesRoot, "better-sqlite3")),
|
||||
betterSqlite3SourceResidueBytes: await sizePathBytes(paths.unpackedRoot, {
|
||||
includeFile: isBetterSqlite3SourceResidue,
|
||||
}),
|
||||
bundledNodeBytes: await sizePathBytes(join(paths.resourceRoot, "bin", "node.exe")),
|
||||
copiedStandaloneNextBytes:
|
||||
await sizePathBytes(join(copiedStandaloneNodeModulesRoot, "next")) +
|
||||
await sizePathBytes(join(copiedStandaloneWebNodeModulesRoot, "next")),
|
||||
copiedStandaloneNextSwcBytes:
|
||||
await sumChildDirectorySizes(join(copiedStandaloneNodeModulesRoot, "@next"), (name) => name.startsWith("swc-win32-")) +
|
||||
await sumChildDirectorySizes(join(copiedStandaloneWebNodeModulesRoot, "@next"), (name) => name.startsWith("swc-win32-")),
|
||||
copiedStandaloneNodeModulesBytes: await sizePathBytes(copiedStandaloneNodeModulesRoot),
|
||||
copiedStandaloneSharpLibvipsBytes: await sizePathBytes(
|
||||
join(copiedStandaloneNodeModulesRoot, "@img", "sharp-libvips-win32-x64"),
|
||||
),
|
||||
copiedStandaloneSourcemapBytes: await sizePathBytes(copiedStandaloneRoot, {
|
||||
includeFile: (path) => path.endsWith(".map"),
|
||||
}),
|
||||
copiedStandaloneTsbuildInfoBytes: await sizePathBytes(copiedStandaloneRoot, {
|
||||
includeFile: (path) => path.endsWith(".tsbuildinfo"),
|
||||
}),
|
||||
copiedStandaloneWebNextBytes: await sizePathBytes(join(copiedStandaloneWebNodeModulesRoot, "next")),
|
||||
copiedStandaloneWebNodeModulesBytes: await sizePathBytes(copiedStandaloneWebNodeModulesRoot),
|
||||
electronLocalesBytes: await sizePathBytes(electronLocalesRoot),
|
||||
markdownBytes: await sizePathBytes(paths.unpackedRoot, { includeFile: (path) => path.endsWith(".md") }),
|
||||
nextBytes: await sizePathBytes(join(appNodeModulesRoot, "next")),
|
||||
nextSwcBytes: await sumChildDirectorySizes(join(appNodeModulesRoot, "@next"), (name) => name.startsWith("swc-win32-")),
|
||||
sharpLibvipsBytes: await sizePathBytes(join(appNodeModulesRoot, "@img", "sharp-libvips-win32-x64")),
|
||||
sourcemapBytes: await sizePathBytes(paths.unpackedRoot, { includeFile: (path) => path.endsWith(".map") }),
|
||||
tsbuildInfoBytes: await sizePathBytes(paths.unpackedRoot, { includeFile: (path) => path.endsWith(".tsbuildinfo") }),
|
||||
webCopiedStandaloneBytes: await sizePathBytes(join(appResourcesRoot, WEB_STANDALONE_RESOURCE_NAME)),
|
||||
webNextCacheBytes: await sizePathBytes(join(appNodeModulesRoot, "@open-design", "web", ".next", "cache")),
|
||||
webPackageBytes: await sizePathBytes(join(appNodeModulesRoot, "@open-design", "web")),
|
||||
webPackageStandaloneBytes: await sizePathBytes(join(appNodeModulesRoot, "@open-design", "web", ".next", "standalone")),
|
||||
webCopiedStandaloneBytes: await sizePathBytes(copiedStandaloneRoot),
|
||||
webNextCacheBytes: await sizePathBytes(join(rootWebPackageRoot, ".next", "cache")),
|
||||
webPackageAppBytes: await sizePathBytes(join(rootWebPackageRoot, "app")),
|
||||
webPackageBytes: await sizePathBytes(rootWebPackageRoot),
|
||||
webPackageDistBytes: await sizePathBytes(join(rootWebPackageRoot, "dist")),
|
||||
webPackagePublicBytes: await sizePathBytes(join(rootWebPackageRoot, "public")),
|
||||
webPackageSrcBytes: await sizePathBytes(join(rootWebPackageRoot, "src")),
|
||||
webPackageStandaloneBytes: await sizePathBytes(join(rootWebPackageRoot, ".next", "standalone")),
|
||||
},
|
||||
unpackedBytes: (await pathExists(paths.unpackedRoot)) ? await sizePathBytes(paths.unpackedRoot) : null,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user