Files
next-ai-draw-io/scripts/afterPack.cjs
Dayuan Jiang 89a0e6d475 fix(electron): properly dereference symlinks by copying actual content (#610)
cpSync with dereference:true does NOT convert symlinks to files.
This implements a custom copyDereferenced function that:
- Detects symlinks using lstatSync
- Follows them using statSync
- Copies actual file/directory content instead of symlink

Fixes macOS arm64 codesign failure with electron-builder 26.4.0
which now does ad-hoc signing and runs codesign --verify.
2026-01-18 19:57:38 +09:00

84 lines
2.8 KiB
JavaScript

/**
* electron-builder afterPack hook
* Copies node_modules to the standalone directory in the packaged app
*/
const {
copyFileSync,
existsSync,
lstatSync,
mkdirSync,
readdirSync,
statSync,
} = require("fs")
const path = require("path")
/**
* Copy directory recursively, converting symlinks to regular files/directories.
* This is needed because cpSync with dereference:true does NOT convert symlinks.
* macOS codesign fails if bundle contains symlinks pointing outside the bundle.
*/
function copyDereferenced(src, dst) {
const lstat = lstatSync(src)
if (lstat.isSymbolicLink()) {
// Follow symlink and check what it points to
const stat = statSync(src)
if (stat.isDirectory()) {
// Symlink to directory: recursively copy the directory contents
mkdirSync(dst, { recursive: true })
for (const entry of readdirSync(src)) {
copyDereferenced(path.join(src, entry), path.join(dst, entry))
}
} else {
// Symlink to file: copy the actual file content
mkdirSync(path.join(dst, ".."), { recursive: true })
copyFileSync(src, dst)
}
} else if (lstat.isDirectory()) {
mkdirSync(dst, { recursive: true })
for (const entry of readdirSync(src)) {
copyDereferenced(path.join(src, entry), path.join(dst, entry))
}
} else {
mkdirSync(path.join(dst, ".."), { recursive: true })
copyFileSync(src, dst)
}
}
module.exports = async (context) => {
const appOutDir = context.appOutDir
const resourcesDir = path.join(
appOutDir,
context.packager.platform.name === "mac"
? `${context.packager.appInfo.productFilename}.app/Contents/Resources`
: "resources",
)
const standaloneDir = path.join(resourcesDir, "standalone")
const sourceNodeModules = path.join(
context.packager.projectDir,
"electron-standalone",
"node_modules",
)
const targetNodeModules = path.join(standaloneDir, "node_modules")
console.log(`[afterPack] Copying node_modules to ${targetNodeModules}`)
if (existsSync(sourceNodeModules) && existsSync(standaloneDir)) {
copyDereferenced(sourceNodeModules, targetNodeModules)
console.log("[afterPack] node_modules copied successfully")
} else {
console.error("[afterPack] Source or target directory not found!")
console.error(
` Source: ${sourceNodeModules} exists: ${existsSync(sourceNodeModules)}`,
)
console.error(
` Target dir: ${standaloneDir} exists: ${existsSync(standaloneDir)}`,
)
throw new Error(
"[afterPack] Failed: Required directories not found. " +
"Ensure 'npm run electron:prepare' was run before building.",
)
}
}