Initial commit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dinlo
2026-05-31 18:44:04 +08:00
commit 436a9631fc
8616 changed files with 1389957 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
import { Stats } from "fs-extra";
export interface ReadAsarHeader {
readonly header: string;
readonly size: number;
}
export interface NodeIntegrity {
algorithm: "SHA256";
hash: string;
blockSize: number;
blocks: Array<string>;
}
export declare class Node {
files?: Record<string, Node>;
unpacked?: boolean;
size?: number;
offset?: string;
executable?: boolean;
link?: string;
integrity?: NodeIntegrity;
}
export declare class AsarFilesystem {
readonly src: string;
readonly header: Node;
readonly headerSize: number;
private offset;
constructor(src: string, header?: Node, headerSize?: number);
searchNodeFromDirectory(p: string, isCreate: boolean): Node | null;
getOrCreateNode(p: string): Node;
addFileNode(file: string, dirNode: Node, size: number, unpacked: boolean, stat: Stats, integrity?: NodeIntegrity): Node;
getNode(p: string): Node | null;
getFile(p: string, followLinks?: boolean): Node;
readJson(file: string): Promise<any>;
readFile(file: string): Promise<Buffer>;
private newNode;
}
export declare function readAsarHeader(archive: string): Promise<ReadAsarHeader>;
export declare function readAsar(archive: string): Promise<AsarFilesystem>;
export declare function readAsarJson(archive: string, file: string): Promise<any>;
+163
View File
@@ -0,0 +1,163 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsarFilesystem = exports.Node = void 0;
exports.readAsarHeader = readAsarHeader;
exports.readAsar = readAsar;
exports.readAsarJson = readAsarJson;
const chromium_pickle_js_1 = require("chromium-pickle-js");
const fs_extra_1 = require("fs-extra");
const path = require("path");
class Node {
}
exports.Node = Node;
class AsarFilesystem {
constructor(src, header = new Node(), headerSize = -1) {
this.src = src;
this.header = header;
this.headerSize = headerSize;
this.offset = 0;
if (this.header.files == null) {
this.header.files = this.newNode();
}
}
searchNodeFromDirectory(p, isCreate) {
let node = this.header;
for (const dir of p.split(path.sep)) {
if (dir !== ".") {
if (node == null) {
throw new Error(`Cannot find node for path: ${p} (node is null at ${dir})`);
}
let child = node.files[dir];
if (child == null) {
if (!isCreate) {
return null;
}
child = new Node();
child.files = this.newNode();
node.files[dir] = child;
}
node = child;
}
}
return node;
}
getOrCreateNode(p) {
if (p == null || p.length === 0) {
return this.header;
}
const name = path.basename(p);
const dirNode = this.searchNodeFromDirectory(path.dirname(p), true);
if (dirNode == null) {
throw new Error(`Cannot find node for path: ${p} (node is null at ${path.dirname(p)})`);
}
if (dirNode.files == null) {
dirNode.files = this.newNode();
}
let result = dirNode.files[name];
if (result == null) {
result = new Node();
dirNode.files[name] = result;
}
return result;
}
addFileNode(file, dirNode, size, unpacked, stat, integrity) {
if (size > 4294967295) {
throw new Error(`${file}: file size cannot be larger than 4.2GB`);
}
const node = new Node();
node.size = size;
if (integrity) {
node.integrity = integrity;
}
if (unpacked) {
node.unpacked = true;
}
else {
// electron expects string
node.offset = this.offset.toString();
if (process.platform !== "win32" && stat.mode & 0o100) {
node.executable = true;
}
this.offset += node.size;
}
let children = dirNode.files;
if (children == null) {
children = this.newNode();
dirNode.files = children;
}
children[path.basename(file)] = node;
return node;
}
getNode(p) {
const node = this.searchNodeFromDirectory(path.dirname(p), false);
if (node == null) {
throw new Error(`Cannot find node for path: ${p} (node is null at ${path.dirname(p)})`);
}
return node.files[path.basename(p)];
}
getFile(p, followLinks = true) {
const info = this.getNode(p);
// if followLinks is false we don't resolve symlinks
return followLinks && info.link != null ? this.getFile(info.link) : info;
}
async readJson(file) {
return JSON.parse((await this.readFile(file)).toString());
}
readFile(file) {
return readFileFromAsar(this, file, this.getFile(file));
}
newNode() {
return Object.create(null);
}
}
exports.AsarFilesystem = AsarFilesystem;
async function readAsarHeader(archive) {
const fd = await (0, fs_extra_1.open)(archive, "r");
let size;
let headerBuf;
try {
const sizeBuf = Buffer.allocUnsafe(8);
if ((await (0, fs_extra_1.read)(fd, sizeBuf, 0, 8, null)).bytesRead !== 8) {
throw new Error("Unable to read header size");
}
const sizePickle = (0, chromium_pickle_js_1.createFromBuffer)(sizeBuf);
size = sizePickle.createIterator().readUInt32();
headerBuf = Buffer.allocUnsafe(size);
if ((await (0, fs_extra_1.read)(fd, headerBuf, 0, size, null)).bytesRead !== size) {
throw new Error("Unable to read header");
}
}
finally {
await (0, fs_extra_1.close)(fd);
}
const headerPickle = (0, chromium_pickle_js_1.createFromBuffer)(headerBuf);
return { header: headerPickle.createIterator().readString(), size };
}
async function readAsar(archive) {
const { header, size } = await readAsarHeader(archive);
return new AsarFilesystem(archive, JSON.parse(header), size);
}
async function readAsarJson(archive, file) {
const fs = await readAsar(archive);
return await fs.readJson(file);
}
async function readFileFromAsar(filesystem, filename, info) {
const size = info.size;
const buffer = Buffer.allocUnsafe(size);
if (size <= 0) {
return buffer;
}
if (info.unpacked) {
return await (0, fs_extra_1.readFile)(path.join(`${filesystem.src}.unpacked`, filename));
}
const fd = await (0, fs_extra_1.open)(filesystem.src, "r");
try {
const offset = 8 + filesystem.headerSize + parseInt(info.offset, 10);
await (0, fs_extra_1.read)(fd, buffer, 0, size, offset);
}
finally {
await (0, fs_extra_1.close)(fd);
}
return buffer;
}
//# sourceMappingURL=asar.js.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
import { FilesystemEntry } from "@electron/asar/lib/filesystem";
export declare function checkFileInArchive(asarFile: string, relativeFile: string, messagePrefix: string): FilesystemEntry;
+24
View File
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkFileInArchive = checkFileInArchive;
const asar = require("@electron/asar");
function checkFileInArchive(asarFile, relativeFile, messagePrefix) {
function error(text) {
return new Error(`${messagePrefix} "${relativeFile}" in the "${asarFile}" ${text}`);
}
let stat;
try {
stat = asar.statFile(asarFile, relativeFile, false);
}
catch (e) {
if (e.message.includes("Cannot read properties of undefined (reading 'link')")) {
throw error("does not exist. Seems like a wrong configuration.");
}
throw error(`is corrupted: ${e}`);
}
if (stat.size === 0) {
throw error(`is corrupted: size 0`);
}
return stat;
}
//# sourceMappingURL=asarFileChecker.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"asarFileChecker.js","sourceRoot":"","sources":["../../src/asar/asarFileChecker.ts"],"names":[],"mappings":";;AAGA,gDAiBC;AApBD,uCAAsC;AAGtC,SAAgB,kBAAkB,CAAC,QAAgB,EAAE,YAAoB,EAAE,aAAqB;IAC9F,SAAS,KAAK,CAAC,IAAY;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,aAAa,KAAK,YAAY,aAAa,QAAQ,KAAK,IAAI,EAAE,CAAC,CAAA;IACrF,CAAC;IACD,IAAI,IAAqB,CAAA;IACzB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,sDAAsD,CAAC,EAAE,CAAC;YAC/E,MAAM,KAAK,CAAC,mDAAmD,CAAC,CAAA;QAClE,CAAC;QACD,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAA;IACnC,CAAC;IACD,IAAK,IAA4B,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAA;IACrC,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import * as asar from \"@electron/asar\"\nimport { FilesystemEntry, FilesystemFileEntry } from \"@electron/asar/lib/filesystem\"\n\nexport function checkFileInArchive(asarFile: string, relativeFile: string, messagePrefix: string) {\n function error(text: string) {\n return new Error(`${messagePrefix} \"${relativeFile}\" in the \"${asarFile}\" ${text}`)\n }\n let stat: FilesystemEntry\n try {\n stat = asar.statFile(asarFile, relativeFile, false)\n } catch (e: any) {\n if (e.message.includes(\"Cannot read properties of undefined (reading 'link')\")) {\n throw error(\"does not exist. Seems like a wrong configuration.\")\n }\n throw error(`is corrupted: ${e}`)\n }\n if ((stat as FilesystemFileEntry).size === 0) {\n throw error(`is corrupted: size 0`)\n }\n return stat\n}\n"]}
+1
View File
@@ -0,0 +1 @@
export {};
+284
View File
@@ -0,0 +1,284 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsarPackager = void 0;
const asar_1 = require("@electron/asar");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs = require("fs-extra");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const appFileCopier_1 = require("../util/appFileCopier");
const unpackDetector_1 = require("./unpackDetector");
const stream_1 = require("stream");
const os = require("os");
const resolvePath = async (file) => (file && (await (0, fs_1.exists)(file)) ? fs.realpath(file).catch(() => path.resolve(file)) : undefined);
const resolvePaths = async (filepaths) => {
return Promise.all(filepaths.map(resolvePath)).then(paths => paths.filter((it) => it != null));
};
const DENYLIST = resolvePaths([
"/usr",
"/lib",
"/bin",
"/sbin",
"/etc",
"/tmp",
"/var", // block whole /var by default. If $HOME is under /var, it's explicitly in ALLOWLIST - https://github.com/electron-userland/electron-builder/issues/9025#issuecomment-3575380041
// macOS system directories
"/System",
"/Library",
"/private",
// Windows system directories
process.env.SystemRoot,
process.env.WINDIR,
]);
const ALLOWLIST = resolvePaths([
os.tmpdir(), // always allow temp dir
os.homedir(), // always allow home dir
]);
/** @internal */
class AsarPackager {
constructor(packager, config) {
this.packager = packager;
this.config = config;
this.outFile = path.join(config.resourcePath, `app.asar`);
}
async pack(fileSets) {
const orderedFileSets = [
// Write dependencies first to minimize offset changes to asar header
...fileSets.slice(1),
// Finish with the app files that change most often
fileSets[0],
].map(set => this.orderFileSet(set));
const streams = await this.processFileSets(orderedFileSets);
await this.executeElectronAsar(streams);
}
async executeElectronAsar(streams) {
// override logger temporarily to clean up console (electron/asar does some internal logging that blogs up the default electron-builder logs)
const consoleLogger = console.log;
console.log = (...args) => {
if (args[0] === "Ordering file has 100% coverage.") {
return; // no need to log, this means our ordering logic is working correctly
}
builder_util_1.log.info({ args }, "logging @electron/asar");
};
await (0, asar_1.createPackageFromStreams)(this.outFile, streams);
console.log = consoleLogger;
}
async processFileSets(fileSets) {
var _a;
const unpackedPaths = new Set();
if (this.config.options.smartUnpack !== false) {
for (const fileSet of fileSets) {
(0, unpackDetector_1.detectUnpackedDirs)(fileSet, unpackedPaths);
}
}
const resultsMap = new Map();
const streamOrdering = [];
const normalizedUnpackedPaths = Array.from(unpackedPaths).map(p => path.normalize(p));
// Check whether a file or directory should be unpacked, using pre-normalized unpacked paths and early returns
const isUnpacked = (dir, file, stat) => {
var _a, _b;
const normalizedDir = path.normalize(dir);
// Check file pattern first (most specific)
if (!(0, builder_util_1.isEmptyOrSpaces)(file) && stat && ((_b = (_a = this.config).unpackPattern) === null || _b === void 0 ? void 0 : _b.call(_a, file, stat))) {
return true;
}
// Check if path is within any unpacked directory
for (const unpackedPath of normalizedUnpackedPaths) {
if (normalizedDir === unpackedPath || normalizedDir.startsWith(unpackedPath + path.sep)) {
return true;
}
}
return false;
};
// First pass: process all files in order, ensuring parent directories exist
for (const fileSet of fileSets) {
// Don't use Promise.all, we need to retain order of execution/iteration through the already-ordered fileset
for (const [index, file] of fileSet.files.entries()) {
const transformedData = (_a = fileSet.transformedFiles) === null || _a === void 0 ? void 0 : _a.get(index);
const stat = fileSet.metadata.get(file);
const destination = path.relative(this.config.defaultDestination, (0, appFileCopier_1.getDestinationPath)(file, fileSet));
// Ensure parent directories exist before processing file
this.ensureParentDirectories(destination, resultsMap, streamOrdering);
const result = await this.processFileOrSymlink({
file,
destination,
fileSet,
transformedData,
stat,
isUnpacked,
});
if (result && !resultsMap.has(result.path)) {
resultsMap.set(result.path, result);
streamOrdering.push(result.path);
}
}
}
// Second pass: propagate unpacked flag to parent directories
for (const entry of resultsMap.values()) {
if (entry.unpacked) {
this.markParentDirectoriesAsUnpacked(entry.path, resultsMap, isUnpacked);
}
}
// Build final results array maintaining processing order
return streamOrdering.reduce((streams, path) => {
const stream = resultsMap.has(path) ? resultsMap.get(path) : null;
if (stream != null) {
streams.push(stream);
}
return streams;
}, []);
}
ensureParentDirectories(destination, resultsMap, streamOrdering) {
const parents = [];
let current = path.dirname(path.normalize(destination));
// Collect all parent directories from deepest to root
while (current !== ".") {
parents.unshift(current);
current = path.dirname(current);
}
// Add parent directories in order (root to deepest)
for (const parentPath of parents) {
if (!resultsMap.has(parentPath)) {
const dir = {
type: "directory",
path: parentPath,
unpacked: false, // Updated in second pass if needed
};
resultsMap.set(parentPath, dir);
streamOrdering.push(parentPath);
}
}
}
markParentDirectoriesAsUnpacked(destination, resultsMap, isUnpacked) {
let current = path.dirname(path.normalize(destination));
while (current !== ".") {
const entry = resultsMap.get(current);
if (entry && isUnpacked(current)) {
entry.unpacked = true;
}
current = path.dirname(current);
}
}
async processFileOrSymlink(options) {
const { isUnpacked, transformedData, file, destination, stat } = options;
const unpacked = isUnpacked(destination, file, stat);
// Handle directories
if (!stat.isFile() && !stat.isSymbolicLink()) {
return { path: destination, unpacked, type: "directory" };
}
// Handle transformed data (pre-processed content)
if (transformedData != null) {
const size = Buffer.byteLength(transformedData);
return {
path: destination,
streamGenerator: () => new stream_1.Readable({
read() {
this.push(transformedData);
this.push(null);
},
}),
unpacked,
type: "file",
stat: { mode: stat.mode, size },
};
}
// verify that the file is not a direct link or symlinked to access/copy a system file
await this.protectSystemAndUnsafePaths(file, await this.packager.info.getWorkspaceRoot());
const baseConfig = {
path: destination,
streamGenerator: () => fs.createReadStream(file),
unpacked,
stat,
};
// Handle regular files
if (!stat.isSymbolicLink()) {
return { ...baseConfig, type: "file" };
}
// Handle symlinks - make relative to source location
let link = await (0, fs_extra_1.readlink)(file);
if (path.isAbsolute(link)) {
link = path.relative(path.dirname(file), link);
}
return {
...baseConfig,
type: "link",
symlink: link,
};
}
orderFileSet(fileSet) {
const sortedFileEntries = Array.from(fileSet.files.entries());
sortedFileEntries.sort(([, a], [, b]) => {
if (a === b) {
return 0;
}
// Place addons last because their signature changes per build
const isAAddon = a.endsWith(".node");
const isBAddon = b.endsWith(".node");
if (isAAddon && !isBAddon) {
return 1;
}
if (isBAddon && !isAAddon) {
return -1;
}
// Otherwise order by name
return a < b ? -1 : 1;
});
let transformedFiles;
if (fileSet.transformedFiles) {
transformedFiles = new Map();
const indexMap = new Map();
for (const [newIndex, [oldIndex]] of sortedFileEntries.entries()) {
indexMap.set(oldIndex, newIndex);
}
for (const [oldIndex, value] of fileSet.transformedFiles) {
const newIndex = indexMap.get(oldIndex);
if (newIndex === undefined) {
throw new Error(`Internal error: ${fileSet.files[oldIndex]} was lost while ordering asar`);
}
transformedFiles.set(newIndex, value);
}
}
return {
src: fileSet.src,
destination: fileSet.destination,
metadata: fileSet.metadata,
files: sortedFileEntries.map(([, file]) => file),
transformedFiles,
};
}
async checkAgainstRoots(target, allowRoots) {
const resolved = await resolvePath(target);
if (resolved == null || (0, builder_util_1.isEmptyOrSpaces)(resolved)) {
return false;
}
for (const root of allowRoots) {
if (resolved === root || resolved.startsWith(root + path.sep)) {
return true;
}
}
return false;
}
async protectSystemAndUnsafePaths(file, workspaceRoot) {
const resolved = await resolvePath(file);
const logFields = { source: file, realPath: resolved };
const workspace = await resolvePath(workspaceRoot);
// If in workspace, always safe
if (workspace && (resolved === null || resolved === void 0 ? void 0 : resolved.startsWith(workspace))) {
return;
}
// Check allowlist (priority)
if (await this.checkAgainstRoots(file, await ALLOWLIST)) {
return;
}
// Check denylist
if (await this.checkAgainstRoots(file, await DENYLIST)) {
builder_util_1.log.error(logFields, `denied access to system or unsafe path`);
throw new Error(`Cannot copy file [${file}] symlinked to file [${resolved}] outside the package to a system or unsafe path`);
}
// Default: outside explicit paths but not explicitly denied
builder_util_1.log.debug(logFields, `path is outside of explicit safe paths, defaulting to safe`);
}
}
exports.AsarPackager = AsarPackager;
//# sourceMappingURL=asarUtil.js.map
File diff suppressed because one or more lines are too long
+15
View File
@@ -0,0 +1,15 @@
import { FileMatcher } from "../fileMatcher";
export interface AsarIntegrityOptions {
readonly resourcesPath: string;
readonly resourcesRelativePath: string;
readonly resourcesDestinationPath: string;
readonly extraResourceMatchers: Array<FileMatcher> | null;
}
export interface HeaderHash {
algorithm: "SHA256";
hash: string;
}
export interface AsarIntegrity {
[key: string]: HeaderHash;
}
export declare function computeData({ resourcesPath, resourcesRelativePath, resourcesDestinationPath, extraResourceMatchers }: AsarIntegrityOptions): Promise<AsarIntegrity>;
+62
View File
@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeData = computeData;
const builder_util_1 = require("builder-util");
const crypto_1 = require("crypto");
const promises_1 = require("fs/promises");
const path = require("path");
const asar_1 = require("./asar");
async function computeData({ resourcesPath, resourcesRelativePath, resourcesDestinationPath, extraResourceMatchers }) {
const isAsar = (filepath) => filepath.endsWith(".asar");
const resources = await (0, promises_1.readdir)(resourcesPath);
const resourceAsars = resources.filter(isAsar).reduce((prev, filename) => ({
...prev,
[path.join(resourcesRelativePath, filename)]: path.join(resourcesPath, filename),
}), {});
const extraResources = await Promise.all((extraResourceMatchers !== null && extraResourceMatchers !== void 0 ? extraResourceMatchers : []).map(async (matcher) => {
const { from, to } = matcher;
const stat = await (0, builder_util_1.statOrNull)(from);
if (stat == null) {
builder_util_1.log.warn({ from }, `file source doesn't exist`);
return [];
}
if (stat.isFile()) {
return [{ from, to }];
}
if (matcher.isEmpty() || matcher.containsOnlyIgnore()) {
matcher.prependPattern("**/*");
}
const matcherFilter = matcher.createFilter();
const extraResourceMatches = await (0, builder_util_1.walk)(matcher.from, (file, stats) => matcherFilter(file, stats) || stats.isDirectory());
return extraResourceMatches.map(from => ({ from, to: matcher.to }));
}));
const extraResourceAsars = extraResources
.flat(1)
.filter(match => isAsar(match.from))
.reduce((prev, { to, from }) => {
const prefix = path.relative(resourcesDestinationPath, to);
return {
...prev,
[path.join(resourcesRelativePath, prefix, path.basename(from))]: from,
};
}, {});
// sort to produce constant result
const allAsars = [...Object.entries(resourceAsars), ...Object.entries(extraResourceAsars)].sort(([name1], [name2]) => name1.localeCompare(name2));
const hashes = await Promise.all(allAsars.map(async ([, from]) => hashHeader(from)));
const asarIntegrity = {};
for (let i = 0; i < allAsars.length; i++) {
const [asar] = allAsars[i];
asarIntegrity[asar] = hashes[i];
}
return asarIntegrity;
}
async function hashHeader(file) {
const hash = (0, crypto_1.createHash)("sha256");
const { header } = await (0, asar_1.readAsarHeader)(file);
hash.update(header);
return {
algorithm: "SHA256",
hash: hash.digest("hex"),
};
}
//# sourceMappingURL=integrity.js.map
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
export declare function isLibOrExe(file: string): boolean;
+43
View File
@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isLibOrExe = isLibOrExe;
exports.detectUnpackedDirs = detectUnpackedDirs;
const builder_util_1 = require("builder-util");
const isbinaryfile_1 = require("isbinaryfile");
const path = require("path");
function isLibOrExe(file) {
// https://github.com/electron-userland/electron-builder/issues/3038
return file.endsWith(".dll") || file.endsWith(".exe") || file.endsWith(".dylib") || file.endsWith(".so") || file.endsWith(".node");
}
/** @internal */
function detectUnpackedDirs(fileSet, autoUnpackDirs) {
const metadata = fileSet.metadata;
for (let i = 0, n = fileSet.files.length; i < n; i++) {
const file = fileSet.files[i];
const stat = metadata.get(file);
if (!stat.moduleRootPath || autoUnpackDirs.has(stat.moduleRootPath)) {
continue;
}
if (!stat.isFile()) {
continue;
}
// https://github.com/electron-userland/electron-builder/issues/2679
let shouldUnpack = false;
// ffprobe-static and ffmpeg-static are known packages to always unpack
const moduleName = stat.moduleName;
const fileBaseName = path.basename(file);
const hasExtension = path.extname(fileBaseName);
if (moduleName === "ffprobe-static" || moduleName === "ffmpeg-static" || isLibOrExe(file)) {
shouldUnpack = true;
}
else if (!hasExtension) {
shouldUnpack = !!(0, isbinaryfile_1.isBinaryFileSync)(file);
}
if (!shouldUnpack) {
continue;
}
builder_util_1.log.debug({ file: stat.moduleFullFilePath, reason: "contains executable code" }, "not packed into asar archive");
autoUnpackDirs.add(stat.moduleRootPath);
}
}
//# sourceMappingURL=unpackDetector.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"unpackDetector.js","sourceRoot":"","sources":["../../src/asar/unpackDetector.ts"],"names":[],"mappings":";;AAKA,gCAGC;AAGD,gDAiCC;AA5CD,+CAA+C;AAC/C,+CAA+C;AAC/C,6BAA4B;AAG5B,SAAgB,UAAU,CAAC,IAAY;IACrC,oEAAoE;IACpE,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACpI,CAAC;AAED,gBAAgB;AAChB,SAAgB,kBAAkB,CAAC,OAAwB,EAAE,cAA2B;IACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC7B,MAAM,IAAI,GAAgB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAE,CAAA;QAC7C,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACpE,SAAQ;QACV,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,SAAQ;QACV,CAAC;QAED,oEAAoE;QACpE,IAAI,YAAY,GAAG,KAAK,CAAA;QACxB,uEAAuE;QACvE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QAClC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACxC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAC/C,IAAI,UAAU,KAAK,gBAAgB,IAAI,UAAU,KAAK,eAAe,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1F,YAAY,GAAG,IAAI,CAAA;QACrB,CAAC;aAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACzB,YAAY,GAAG,CAAC,CAAC,IAAA,+BAAgB,EAAC,IAAI,CAAC,CAAA;QACzC,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,SAAQ;QACV,CAAC;QAED,kBAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,EAAE,0BAA0B,EAAE,EAAE,8BAA8B,CAAC,CAAA;QAChH,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IACzC,CAAC;AACH,CAAC","sourcesContent":["import { FilterStats, log } from \"builder-util\"\nimport { isBinaryFileSync } from \"isbinaryfile\"\nimport * as path from \"path\"\nimport { ResolvedFileSet } from \"../util/appFileCopier\"\n\nexport function isLibOrExe(file: string): boolean {\n // https://github.com/electron-userland/electron-builder/issues/3038\n return file.endsWith(\".dll\") || file.endsWith(\".exe\") || file.endsWith(\".dylib\") || file.endsWith(\".so\") || file.endsWith(\".node\")\n}\n\n/** @internal */\nexport function detectUnpackedDirs(fileSet: ResolvedFileSet, autoUnpackDirs: Set<string>) {\n const metadata = fileSet.metadata\n\n for (let i = 0, n = fileSet.files.length; i < n; i++) {\n const file = fileSet.files[i]\n const stat: FilterStats = metadata.get(file)!\n if (!stat.moduleRootPath || autoUnpackDirs.has(stat.moduleRootPath)) {\n continue\n }\n\n if (!stat.isFile()) {\n continue\n }\n\n // https://github.com/electron-userland/electron-builder/issues/2679\n let shouldUnpack = false\n // ffprobe-static and ffmpeg-static are known packages to always unpack\n const moduleName = stat.moduleName\n const fileBaseName = path.basename(file)\n const hasExtension = path.extname(fileBaseName)\n if (moduleName === \"ffprobe-static\" || moduleName === \"ffmpeg-static\" || isLibOrExe(file)) {\n shouldUnpack = true\n } else if (!hasExtension) {\n shouldUnpack = !!isBinaryFileSync(file)\n }\n\n if (!shouldUnpack) {\n continue\n }\n\n log.debug({ file: stat.moduleFullFilePath, reason: \"contains executable code\" }, \"not packed into asar archive\")\n autoUnpackDirs.add(stat.moduleRootPath)\n }\n}\n"]}