asarUtil.js 10.9 KB
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.AsarPackager = void 0;

function _builderUtil() {
  const data = require("builder-util");

  _builderUtil = function () {
    return data;
  };

  return data;
}

function _fs() {
  const data = require("builder-util/out/fs");

  _fs = function () {
    return data;
  };

  return data;
}

var _fs2 = require("fs");

function _fsExtra() {
  const data = require("fs-extra");

  _fsExtra = function () {
    return data;
  };

  return data;
}

var path = _interopRequireWildcard(require("path"));

function _appFileCopier() {
  const data = require("../util/appFileCopier");

  _appFileCopier = function () {
    return data;
  };

  return data;
}

function _asar() {
  const data = require("./asar");

  _asar = function () {
    return data;
  };

  return data;
}

function _unpackDetector() {
  const data = require("./unpackDetector");

  _unpackDetector = function () {
    return data;
  };

  return data;
}

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

// eslint-disable-next-line @typescript-eslint/no-var-requires
const pickle = require("chromium-pickle-js");
/** @internal */


class AsarPackager {
  constructor(src, destination, options, unpackPattern) {
    this.src = src;
    this.destination = destination;
    this.options = options;
    this.unpackPattern = unpackPattern;
    this.fs = new (_asar().AsarFilesystem)(this.src);
    this.outFile = path.join(destination, "app.asar");
    this.unpackedDest = `${this.outFile}.unpacked`;
  } // sort files to minimize file change (i.e. asar file is not changed dramatically on small change)


  async pack(fileSets, packager) {
    if (this.options.ordering != null) {
      // ordering doesn't support transformed files, but ordering is not used functionality - wait user report to fix it
      await order(fileSets[0].files, this.options.ordering, fileSets[0].src);
    }

    await (0, _fsExtra().ensureDir)(path.dirname(this.outFile));
    const unpackedFileIndexMap = new Map();

    for (const fileSet of fileSets) {
      unpackedFileIndexMap.set(fileSet, await this.createPackageFromFiles(fileSet, packager.info));
    }

    await this.writeAsarFile(fileSets, unpackedFileIndexMap);
  }

  async createPackageFromFiles(fileSet, packager) {
    const metadata = fileSet.metadata; // search auto unpacked dir

    const unpackedDirs = new Set();
    const rootForAppFilesWithoutAsar = path.join(this.destination, "app");

    if (this.options.smartUnpack !== false) {
      await (0, _unpackDetector().detectUnpackedDirs)(fileSet, unpackedDirs, this.unpackedDest, rootForAppFilesWithoutAsar);
    }

    const dirToCreateForUnpackedFiles = new Set(unpackedDirs);

    const correctDirNodeUnpackedFlag = async (filePathInArchive, dirNode) => {
      for (const dir of unpackedDirs) {
        if (filePathInArchive.length > dir.length + 2 && filePathInArchive[dir.length] === path.sep && filePathInArchive.startsWith(dir)) {
          dirNode.unpacked = true;
          unpackedDirs.add(filePathInArchive); // not all dirs marked as unpacked after first iteration - because node module dir can be marked as unpacked after processing node module dir content
          // e.g. node-notifier/example/advanced.js processed, but only on process vendor/terminal-notifier.app module will be marked as unpacked

          await (0, _fsExtra().ensureDir)(path.join(this.unpackedDest, filePathInArchive));
          break;
        }
      }
    };

    const transformedFiles = fileSet.transformedFiles;
    const taskManager = new (_builderUtil().AsyncTaskManager)(packager.cancellationToken);
    const fileCopier = new (_fs().FileCopier)();
    let currentDirNode = null;
    let currentDirPath = null;
    const unpackedFileIndexSet = new Set();

    for (let i = 0, n = fileSet.files.length; i < n; i++) {
      const file = fileSet.files[i];
      const stat = metadata.get(file);

      if (stat == null) {
        continue;
      }

      const pathInArchive = path.relative(rootForAppFilesWithoutAsar, (0, _appFileCopier().getDestinationPath)(file, fileSet));

      if (stat.isSymbolicLink()) {
        const s = stat;
        this.fs.getOrCreateNode(pathInArchive).link = s.relativeLink;
        s.pathInArchive = pathInArchive;
        unpackedFileIndexSet.add(i);
        continue;
      }

      let fileParent = path.dirname(pathInArchive);

      if (fileParent === ".") {
        fileParent = "";
      }

      if (currentDirPath !== fileParent) {
        if (fileParent.startsWith("..")) {
          throw new Error(`Internal error: path must not start with "..": ${fileParent}`);
        }

        currentDirPath = fileParent;
        currentDirNode = this.fs.getOrCreateNode(fileParent); // do not check for root

        if (fileParent !== "" && !currentDirNode.unpacked) {
          if (unpackedDirs.has(fileParent)) {
            currentDirNode.unpacked = true;
          } else {
            await correctDirNodeUnpackedFlag(fileParent, currentDirNode);
          }
        }
      }

      const dirNode = currentDirNode;
      const newData = transformedFiles == null ? null : transformedFiles.get(i);
      const isUnpacked = dirNode.unpacked || this.unpackPattern != null && this.unpackPattern(file, stat);
      this.fs.addFileNode(file, dirNode, newData == null ? stat.size : Buffer.byteLength(newData), isUnpacked, stat);

      if (isUnpacked) {
        if (!dirNode.unpacked && !dirToCreateForUnpackedFiles.has(fileParent)) {
          dirToCreateForUnpackedFiles.add(fileParent);
          await (0, _fsExtra().ensureDir)(path.join(this.unpackedDest, fileParent));
        }

        const unpackedFile = path.join(this.unpackedDest, pathInArchive);
        taskManager.addTask(copyFileOrData(fileCopier, newData, file, unpackedFile, stat));

        if (taskManager.tasks.length > _fs().MAX_FILE_REQUESTS) {
          await taskManager.awaitTasks();
        }

        unpackedFileIndexSet.add(i);
      }
    }

    if (taskManager.tasks.length > 0) {
      await taskManager.awaitTasks();
    }

    return unpackedFileIndexSet;
  }

  writeAsarFile(fileSets, unpackedFileIndexMap) {
    return new Promise((resolve, reject) => {
      const headerPickle = pickle.createEmpty();
      headerPickle.writeString(JSON.stringify(this.fs.header));
      const headerBuf = headerPickle.toBuffer();
      const sizePickle = pickle.createEmpty();
      sizePickle.writeUInt32(headerBuf.length);
      const sizeBuf = sizePickle.toBuffer();
      const writeStream = (0, _fsExtra().createWriteStream)(this.outFile);
      writeStream.on("error", reject);
      writeStream.on("close", resolve);
      writeStream.write(sizeBuf);
      let fileSetIndex = 0;
      let files = fileSets[0].files;
      let metadata = fileSets[0].metadata;
      let transformedFiles = fileSets[0].transformedFiles;
      let unpackedFileIndexSet = unpackedFileIndexMap.get(fileSets[0]);

      const w = index => {
        while (true) {
          if (index >= files.length) {
            if (++fileSetIndex >= fileSets.length) {
              writeStream.end();
              return;
            } else {
              files = fileSets[fileSetIndex].files;
              metadata = fileSets[fileSetIndex].metadata;
              transformedFiles = fileSets[fileSetIndex].transformedFiles;
              unpackedFileIndexSet = unpackedFileIndexMap.get(fileSets[fileSetIndex]);
              index = 0;
            }
          }

          if (!unpackedFileIndexSet.has(index)) {
            break;
          } else {
            const stat = metadata.get(files[index]);

            if (stat != null && stat.isSymbolicLink()) {
              (0, _fs2.symlink)(stat.linkRelativeToFile, path.join(this.unpackedDest, stat.pathInArchive), () => w(index + 1));
              return;
            }
          }

          index++;
        }

        const data = transformedFiles == null ? null : transformedFiles.get(index);
        const file = files[index];

        if (data !== null && data !== undefined) {
          writeStream.write(data, () => w(index + 1));
          return;
        } // https://github.com/yarnpkg/yarn/pull/3539


        const stat = metadata.get(file);

        if (stat != null && stat.size < 2 * 1024 * 1024) {
          (0, _fsExtra().readFile)(file).then(it => {
            writeStream.write(it, () => w(index + 1));
          }).catch(e => reject(`Cannot read file ${file}: ${e.stack || e}`));
        } else {
          const readStream = (0, _fsExtra().createReadStream)(file);
          readStream.on("error", reject);
          readStream.once("end", () => w(index + 1));
          readStream.on("open", () => {
            readStream.pipe(writeStream, {
              end: false
            });
          });
        }
      };

      writeStream.write(headerBuf, () => w(0));
    });
  }

}

exports.AsarPackager = AsarPackager;

async function order(filenames, orderingFile, src) {
  const orderingFiles = (await (0, _fsExtra().readFile)(orderingFile, "utf8")).split("\n").map(line => {
    if (line.indexOf(":") !== -1) {
      line = line.split(":").pop();
    }

    line = line.trim();

    if (line[0] === "/") {
      line = line.slice(1);
    }

    return line;
  });
  const ordering = [];

  for (const file of orderingFiles) {
    const pathComponents = file.split(path.sep);

    for (const pathComponent of pathComponents) {
      ordering.push(path.join(src, pathComponent));
    }
  }

  const sortedFiles = [];
  let missing = 0;
  const total = filenames.length;

  for (const file of ordering) {
    if (!sortedFiles.includes(file) && filenames.includes(file)) {
      sortedFiles.push(file);
    }
  }

  for (const file of filenames) {
    if (!sortedFiles.includes(file)) {
      sortedFiles.push(file);
      missing += 1;
    }
  }

  _builderUtil().log.info({
    coverage: (total - missing) / total * 100
  }, "ordering files in ASAR archive");

  return sortedFiles;
}

function copyFileOrData(fileCopier, data, source, destination, stats) {
  if (data == null) {
    return fileCopier.copy(source, destination, stats);
  } else {
    return (0, _fsExtra().writeFile)(destination, data);
  }
} 
// __ts-babel@6.0.4
//# sourceMappingURL=asarUtil.js.map