@rushstack/package-extractor
Version:
A library for bundling selected files and dependencies into a deployable package.
171 lines • 8.57 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssetHandler = void 0;
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = __importDefault(require("node:fs"));
const node_core_library_1 = require("@rushstack/node-core-library");
const ArchiveManager_1 = require("./ArchiveManager");
const Utils_1 = require("./Utils");
class AssetHandler {
constructor(options) {
this._includedAssetPaths = new Set();
this._isFinalized = false;
const { terminal, sourceRootFolder, targetRootFolder, linkCreation, symlinkAnalyzer, createArchiveFilePath, createArchiveOnly = false } = options;
this._terminal = terminal;
this._sourceRootFolder = sourceRootFolder;
this._targetRootFolder = targetRootFolder;
this._symlinkAnalyzer = symlinkAnalyzer;
if (createArchiveFilePath) {
if (node_path_1.default.extname(createArchiveFilePath) !== '.zip') {
throw new Error('Only archives with the .zip file extension are currently supported.');
}
this._archiveFilePath = node_path_1.default.resolve(targetRootFolder, createArchiveFilePath);
this._archiveManager = new ArchiveManager_1.ArchiveManager();
}
if (createArchiveOnly && !this._archiveManager) {
throw new Error('createArchiveOnly cannot be true if createArchiveFilePath is not provided');
}
this._createArchiveOnly = createArchiveOnly;
this._linkCreationMode = linkCreation || 'default';
}
async includeAssetAsync(options) {
const { sourceFileContent, targetFilePath, ignoreIfExisting = false } = options;
let { sourceFilePath } = options;
if (this._isFinalized) {
throw new Error('includeAssetAsync() cannot be called after finalizeAsync()');
}
if (!sourceFilePath && !sourceFileContent) {
if (!node_core_library_1.Path.isUnder(targetFilePath, this._targetRootFolder)) {
throw new Error('The existing asset path must be under the target root folder');
}
sourceFilePath = targetFilePath;
}
if (sourceFilePath && sourceFileContent) {
throw new Error('Either sourceFilePath or sourceFileContent must be provided, but not both');
}
if (this._includedAssetPaths.has(targetFilePath)) {
if (ignoreIfExisting) {
return;
}
throw new Error(`The asset at path "${targetFilePath}" has already been included`);
}
if (!this._createArchiveOnly) {
// Ignore when the source file is the same as the target file, as it's a no-op
if (sourceFilePath && sourceFilePath !== targetFilePath) {
// Use the fs.copyFile API instead of FileSystem.copyFileAsync() since copyFileAsync performs
// a needless stat() call to determine if it's a file or folder, and we already know it's a file.
try {
await node_fs_1.default.promises.copyFile(sourceFilePath, targetFilePath, node_fs_1.default.constants.COPYFILE_EXCL);
}
catch (e) {
if (!node_core_library_1.FileSystem.isNotExistError(e)) {
throw e;
}
// The parent folder may not exist, so ensure it exists before trying to copy again
await node_core_library_1.FileSystem.ensureFolderAsync(node_path_1.default.dirname(targetFilePath));
await node_fs_1.default.promises.copyFile(sourceFilePath, targetFilePath, node_fs_1.default.constants.COPYFILE_EXCL);
}
}
else if (sourceFileContent) {
await node_core_library_1.FileSystem.writeFileAsync(targetFilePath, sourceFileContent, {
ensureFolderExists: true
});
}
}
if (this._archiveManager) {
const targetRelativeFilePath = node_path_1.default.relative(this._targetRootFolder, targetFilePath);
if (sourceFilePath) {
await this._archiveManager.addToArchiveAsync({
filePath: sourceFilePath,
archivePath: targetRelativeFilePath
});
}
else if (sourceFileContent) {
await this._archiveManager.addToArchiveAsync({
fileData: sourceFileContent,
archivePath: targetRelativeFilePath
});
}
}
this._includedAssetPaths.add(targetFilePath);
}
get assetPaths() {
return [...this._includedAssetPaths];
}
async finalizeAsync(options) {
const { onAfterExtractSymlinksAsync } = options !== null && options !== void 0 ? options : {};
if (this._isFinalized) {
throw new Error('finalizeAsync() has already been called');
}
if (this._linkCreationMode === 'default') {
this._terminal.writeLine('Creating symlinks');
const linksToCopy = this._symlinkAnalyzer.reportSymlinks();
await node_core_library_1.Async.forEachAsync(linksToCopy, async (linkToCopy) => {
await this._extractSymlinkAsync(linkToCopy);
});
}
await (onAfterExtractSymlinksAsync === null || onAfterExtractSymlinksAsync === void 0 ? void 0 : onAfterExtractSymlinksAsync());
if (this._archiveManager && this._archiveFilePath) {
this._terminal.writeLine(`Creating archive at "${this._archiveFilePath}"`);
await this._archiveManager.createArchiveAsync(this._archiveFilePath);
}
this._isFinalized = true;
}
/**
* Create a symlink as described by the ILinkInfo object.
*/
async _extractSymlinkAsync(linkInfo) {
const { kind, linkPath, targetPath } = {
...linkInfo,
linkPath: (0, Utils_1.remapSourcePathForTargetFolder)({
sourceRootFolder: this._sourceRootFolder,
targetRootFolder: this._targetRootFolder,
sourcePath: linkInfo.linkPath
}),
targetPath: (0, Utils_1.remapSourcePathForTargetFolder)({
sourceRootFolder: this._sourceRootFolder,
targetRootFolder: this._targetRootFolder,
sourcePath: linkInfo.targetPath
})
};
const newLinkFolder = node_path_1.default.dirname(linkPath);
await node_core_library_1.FileSystem.ensureFolderAsync(newLinkFolder);
// Link to the relative path for symlinks
const relativeTargetPath = node_path_1.default.relative(newLinkFolder, targetPath);
// NOTE: This logic is based on NpmLinkManager._createSymlink()
if (kind === 'fileLink') {
// For files, we use a Windows "hard link", because creating a symbolic link requires
// administrator permission. However hard links seem to cause build failures on Mac,
// so for all other operating systems we use symbolic links for this case.
if (process.platform === 'win32') {
await node_core_library_1.FileSystem.createHardLinkAsync({
linkTargetPath: relativeTargetPath,
newLinkPath: linkPath
});
}
else {
await node_core_library_1.FileSystem.createSymbolicLinkFileAsync({
linkTargetPath: relativeTargetPath,
newLinkPath: linkPath
});
}
}
else {
// Junctions are only supported on Windows. This will create a symbolic link on other platforms.
await node_core_library_1.FileSystem.createSymbolicLinkJunctionAsync({
linkTargetPath: relativeTargetPath,
newLinkPath: linkPath
});
}
// Since the created symlinks have the required relative paths, they can be added directly to
// the archive.
await this.includeAssetAsync({ targetFilePath: linkPath });
}
}
exports.AssetHandler = AssetHandler;
//# sourceMappingURL=AssetHandler.js.map