UNPKG

@rushstack/package-extractor

Version:

A library for bundling selected files and dependencies into a deployable package.

171 lines 8.57 kB
"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