UNPKG

@rushstack/package-extractor

Version:

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

568 lines 32.2 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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PackageExtractor = exports.TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING = void 0; const path = __importStar(require("path")); const minimatch_1 = require("minimatch"); const semver_1 = __importDefault(require("semver")); const npm_packlist_1 = __importDefault(require("npm-packlist")); const ignore_1 = __importDefault(require("ignore")); const node_core_library_1 = require("@rushstack/node-core-library"); const terminal_1 = require("@rushstack/terminal"); const SymlinkAnalyzer_1 = require("./SymlinkAnalyzer"); const AssetHandler_1 = require("./AssetHandler"); const Utils_1 = require("./Utils"); const PathConstants_1 = require("./PathConstants"); const constants_1 = require("./scripts/createLinks/utilities/constants"); exports.TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING = '{TARGET_ROOT_SCRIPT_RELATIVE_PATH}'; /** * Manages the business logic for the "rush deploy" command. * * @public */ class PackageExtractor { /** * Get a list of files that would be included in a package created from the provided package root path. * * @beta */ static async getPackageIncludedFilesAsync(packageRootPath) { // Use npm-packlist to filter the files. Using the Walker class (instead of the default API) ensures // that "bundledDependencies" are not included. const walkerPromise = new Promise((resolve, reject) => { const walker = new npm_packlist_1.default.Walker({ path: packageRootPath }); walker.on('done', resolve).on('error', reject).start(); }); const npmPackFiles = await walkerPromise; return npmPackFiles; } /** * Extract a package using the provided options */ async extractAsync(options) { options = PackageExtractor._normalizeOptions(options); const { terminal, projectConfigurations, sourceRootFolder, targetRootFolder, mainProjectName, overwriteExisting, dependencyConfigurations, linkCreation } = options; terminal.writeLine(terminal_1.Colorize.cyan(`Extracting to target folder: ${targetRootFolder}`)); terminal.writeLine(terminal_1.Colorize.cyan(`Main project for extraction: ${mainProjectName}`)); await node_core_library_1.FileSystem.ensureFolderAsync(targetRootFolder); const existingExtraction = (await node_core_library_1.FileSystem.readFolderItemNamesAsync(targetRootFolder)).length > 0; if (existingExtraction) { if (!overwriteExisting) { throw new Error('The extraction target folder is not empty. Overwrite must be explicitly requested'); } terminal.writeLine('Deleting target folder contents...'); terminal.writeLine(''); await node_core_library_1.FileSystem.ensureEmptyFolderAsync(targetRootFolder); } // Create a new state for each run const symlinkAnalyzer = new SymlinkAnalyzer_1.SymlinkAnalyzer({ requiredSourceParentPath: sourceRootFolder }); const state = { symlinkAnalyzer, assetHandler: new AssetHandler_1.AssetHandler({ ...options, symlinkAnalyzer }), foldersToCopy: new Set(), packageJsonByPath: new Map(), projectConfigurationsByName: new Map(projectConfigurations.map((p) => [p.projectName, p])), projectConfigurationsByPath: new Map(projectConfigurations.map((p) => [p.projectFolder, p])), dependencyConfigurationsByName: new Map() }; // set state dependencyConfigurationsByName for (const dependencyConfiguration of dependencyConfigurations || []) { const { dependencyName } = dependencyConfiguration; let existingDependencyConfigurations = state.dependencyConfigurationsByName.get(dependencyName); if (!existingDependencyConfigurations) { existingDependencyConfigurations = []; state.dependencyConfigurationsByName.set(dependencyName, existingDependencyConfigurations); } existingDependencyConfigurations.push(dependencyConfiguration); } await this._performExtractionAsync(options, state); await state.assetHandler.finalizeAsync({ onAfterExtractSymlinksAsync: async () => { // We need the symlinks to be created before attempting to create the bin links, since it requires // the node_modules folder to be realized. While we're here, we may as well perform some specific // link creation tasks and write the extractor-metadata.json file before the asset handler finalizes. if (linkCreation === 'default') { await this._makeBinLinksAsync(options, state); } else if (linkCreation === 'script') { await this._writeCreateLinksScriptAsync(options, state); } terminal.writeLine('Creating extractor-metadata.json'); await this._writeExtractorMetadataAsync(options, state); } }); } static _normalizeOptions(options) { if (options.subspaces) { if (options.pnpmInstallFolder !== undefined) { throw new Error('IExtractorOptions.pnpmInstallFolder cannot be combined with IExtractorOptions.subspaces'); } if (options.transformPackageJson !== undefined) { throw new Error('IExtractorOptions.transformPackageJson cannot be combined with IExtractorOptions.subspaces'); } return options; } const normalizedOptions = { ...options }; delete normalizedOptions.pnpmInstallFolder; delete normalizedOptions.transformPackageJson; normalizedOptions.subspaces = [ { subspaceName: 'default', pnpmInstallFolder: options.pnpmInstallFolder, transformPackageJson: options.transformPackageJson } ]; return normalizedOptions; } async _performExtractionAsync(options, state) { const { terminal, mainProjectName, sourceRootFolder, targetRootFolder, folderToCopy: additionalFolderToCopy, createArchiveOnly } = options; const { projectConfigurationsByName, foldersToCopy } = state; const mainProjectConfiguration = projectConfigurationsByName.get(mainProjectName); if (!mainProjectConfiguration) { throw new Error(`Main project "${mainProjectName}" was not found in the list of projects`); } // Calculate the set with additionalProjectsToInclude const includedProjectsSet = new Set([mainProjectConfiguration]); for (const { additionalProjectsToInclude } of includedProjectsSet) { if (additionalProjectsToInclude) { for (const additionalProjectNameToInclude of additionalProjectsToInclude) { const additionalProjectToInclude = projectConfigurationsByName.get(additionalProjectNameToInclude); if (!additionalProjectToInclude) { throw new Error(`Project "${additionalProjectNameToInclude}" was not found in the list of projects.`); } includedProjectsSet.add(additionalProjectToInclude); } } } for (const { projectName, projectFolder } of includedProjectsSet) { terminal.writeLine(terminal_1.Colorize.cyan(`Analyzing project: ${projectName}`)); await this._collectFoldersAsync(projectFolder, options, state); } if (!createArchiveOnly) { terminal.writeLine(`Copying folders to target folder "${targetRootFolder}"`); } await node_core_library_1.Async.forEachAsync(foldersToCopy, async (folderToCopy) => { await this._extractFolderAsync(folderToCopy, options, state); }, { concurrency: constants_1.MAX_CONCURRENCY }); if (additionalFolderToCopy) { // Copy the additional folder directly into the root of the target folder by setting the sourceRootFolder // to the root of the folderToCopy const additionalFolderPath = path.resolve(sourceRootFolder, additionalFolderToCopy); const additionalFolderExtractorOptions = { ...options, sourceRootFolder: additionalFolderPath, targetRootFolder }; await this._extractFolderAsync(additionalFolderPath, additionalFolderExtractorOptions, state); } } /** * Recursively crawl the node_modules dependencies and collect the result in IExtractorState.foldersToCopy. */ async _collectFoldersAsync(packageJsonFolder, options, state) { const { terminal, subspaces } = options; const { projectConfigurationsByPath } = state; const packageJsonFolderPathQueue = new node_core_library_1.AsyncQueue([packageJsonFolder]); await node_core_library_1.Async.forEachAsync(packageJsonFolderPathQueue, async ([packageJsonFolderPath, callback]) => { var _a, _b; const packageJsonRealFolderPath = await node_core_library_1.FileSystem.getRealPathAsync(packageJsonFolderPath); if (state.foldersToCopy.has(packageJsonRealFolderPath)) { // we've already seen this folder callback(); return; } state.foldersToCopy.add(packageJsonRealFolderPath); const originalPackageJson = await node_core_library_1.JsonFile.loadAsync(path.join(packageJsonRealFolderPath, 'package.json')); const targetSubspace = subspaces === null || subspaces === void 0 ? void 0 : subspaces.find((subspace) => subspace.pnpmInstallFolder && node_core_library_1.Path.isUnder(packageJsonFolderPath, subspace.pnpmInstallFolder)); // Transform packageJson using the provided transformer, if requested const packageJson = (_b = (_a = targetSubspace === null || targetSubspace === void 0 ? void 0 : targetSubspace.transformPackageJson) === null || _a === void 0 ? void 0 : _a.call(targetSubspace, originalPackageJson)) !== null && _b !== void 0 ? _b : originalPackageJson; state.packageJsonByPath.set(packageJsonRealFolderPath, packageJson); // Union of keys from regular dependencies, peerDependencies, optionalDependencies // (and possibly devDependencies if includeDevDependencies=true) const dependencyNamesToProcess = new Set(); // Just the keys from optionalDependencies and peerDependencies const optionalDependencyNames = new Set(); for (const name of Object.keys(packageJson.dependencies || {})) { dependencyNamesToProcess.add(name); } for (const name of Object.keys(packageJson.peerDependencies || {})) { dependencyNamesToProcess.add(name); optionalDependencyNames.add(name); // consider peers optional, since they are so frequently broken } for (const name of Object.keys(packageJson.optionalDependencies || {})) { dependencyNamesToProcess.add(name); optionalDependencyNames.add(name); } // Check to see if this is a local project const projectConfiguration = projectConfigurationsByPath.get(packageJsonRealFolderPath); if (projectConfiguration) { if (options.includeDevDependencies) { for (const name of Object.keys(packageJson.devDependencies || {})) { dependencyNamesToProcess.add(name); } } this._applyDependencyFilters(terminal, dependencyNamesToProcess, projectConfiguration.additionalDependenciesToInclude, projectConfiguration.dependenciesToExclude); } for (const dependencyPackageName of dependencyNamesToProcess) { try { const dependencyPackageFolderPath = await node_core_library_1.Import.resolvePackageAsync({ packageName: dependencyPackageName, baseFolderPath: packageJsonRealFolderPath, getRealPathAsync: async (filePath) => { try { return (await state.symlinkAnalyzer.analyzePathAsync({ inputPath: filePath })).nodePath; } catch (error) { if (node_core_library_1.FileSystem.isFileDoesNotExistError(error)) { return filePath; } throw error; } } }); packageJsonFolderPathQueue.push(dependencyPackageFolderPath); } catch (resolveErr) { if (optionalDependencyNames.has(dependencyPackageName)) { // Ignore missing optional dependency continue; } throw resolveErr; } } // Replicate the links to the virtual store. Note that if the package has not been hoisted by // PNPM, the package will not be resolvable from here. // Only apply this logic for packages that were actually installed under the common/temp folder. const realPnpmInstallFolder = targetSubspace === null || targetSubspace === void 0 ? void 0 : targetSubspace.pnpmInstallFolder; if (realPnpmInstallFolder && node_core_library_1.Path.isUnder(packageJsonFolderPath, realPnpmInstallFolder)) { try { // The PNPM virtual store links are created in this folder. We will resolve the current package // from that location and collect any additional links encountered along the way. // TODO: This can be configured via NPMRC. We should support that. const pnpmDotFolderPath = path.join(realPnpmInstallFolder, 'node_modules', '.pnpm'); // TODO: Investigate how package aliases are handled by PNPM in this case. For example: // // "dependencies": { // "alias-name": "npm:real-name@^1.2.3" // } const dependencyPackageFolderPath = await node_core_library_1.Import.resolvePackageAsync({ packageName: packageJson.name, baseFolderPath: pnpmDotFolderPath, getRealPathAsync: async (filePath) => { try { return (await state.symlinkAnalyzer.analyzePathAsync({ inputPath: filePath })).nodePath; } catch (error) { if (node_core_library_1.FileSystem.isFileDoesNotExistError(error)) { return filePath; } throw error; } } }); packageJsonFolderPathQueue.push(dependencyPackageFolderPath); } catch (resolveErr) { // The virtual store link isn't guaranteed to exist, so ignore if it's missing // NOTE: If you encounter this warning a lot, please report it to the Rush maintainers. // eslint-disable-next-line no-console console.log('Ignoring missing PNPM virtual store link for ' + packageJsonFolderPath); } } callback(); }, { concurrency: constants_1.MAX_CONCURRENCY }); } _applyDependencyFilters(terminal, allDependencyNames, additionalDependenciesToInclude = [], dependenciesToExclude = []) { // Track packages that got added/removed for reporting purposes const extraIncludedPackageNames = []; const extraExcludedPackageNames = []; for (const patternWithStar of dependenciesToExclude) { for (const dependency of allDependencyNames) { if ((0, Utils_1.matchesWithStar)(patternWithStar, dependency)) { if (allDependencyNames.delete(dependency)) { extraExcludedPackageNames.push(dependency); } } } } for (const dependencyToInclude of additionalDependenciesToInclude) { if (!allDependencyNames.has(dependencyToInclude)) { allDependencyNames.add(dependencyToInclude); extraIncludedPackageNames.push(dependencyToInclude); } } if (extraIncludedPackageNames.length > 0) { extraIncludedPackageNames.sort(); terminal.writeLine(`Extra dependencies included by settings: ${extraIncludedPackageNames.join(', ')}`); } if (extraExcludedPackageNames.length > 0) { extraExcludedPackageNames.sort(); terminal.writeLine(`Extra dependencies excluded by settings: ${extraExcludedPackageNames.join(', ')}`); } return allDependencyNames; } /** * Copy one package folder to the extractor target folder. */ async _extractFolderAsync(sourceFolderPath, options, state) { const { includeNpmIgnoreFiles } = options; const { projectConfigurationsByPath, packageJsonByPath, dependencyConfigurationsByName, assetHandler } = state; let useNpmIgnoreFilter = false; const sourceFolderRealPath = await node_core_library_1.FileSystem.getRealPathAsync(sourceFolderPath); const sourceProjectConfiguration = projectConfigurationsByPath.get(sourceFolderRealPath); const packagesJson = packageJsonByPath.get(sourceFolderRealPath); // As this function will be used to copy folder for both project inside monorepo and third party // dependencies insides node_modules. Third party dependencies won't have project configurations const isLocalProject = !!sourceProjectConfiguration; // Function to filter files inside local project or third party dependencies. const isFileExcluded = (filePath) => { // Encapsulate exclude logic into a function, so it can be reused. const excludeFileByPatterns = (patternsToInclude, patternsToExclude) => { let includeFilters; let excludeFilters; if (patternsToInclude === null || patternsToInclude === void 0 ? void 0 : patternsToInclude.length) { includeFilters = patternsToInclude === null || patternsToInclude === void 0 ? void 0 : patternsToInclude.map((p) => new minimatch_1.Minimatch(p, { dot: true })); } if (patternsToExclude === null || patternsToExclude === void 0 ? void 0 : patternsToExclude.length) { excludeFilters = patternsToExclude === null || patternsToExclude === void 0 ? void 0 : patternsToExclude.map((p) => new minimatch_1.Minimatch(p, { dot: true })); } // If there are no filters, then we can't exclude anything. if (!includeFilters && !excludeFilters) { return false; } const isIncluded = !includeFilters || includeFilters.some((m) => m.match(filePath)); // If the file is not included, then we don't need to check the excludeFilter. If it is included // and there is no exclude filter, then we know that the file is not excluded. If it is included // and there is an exclude filter, then we need to check for a match. return !isIncluded || !!(excludeFilters === null || excludeFilters === void 0 ? void 0 : excludeFilters.some((m) => m.match(filePath))); }; if (isLocalProject) { return excludeFileByPatterns(sourceProjectConfiguration === null || sourceProjectConfiguration === void 0 ? void 0 : sourceProjectConfiguration.patternsToInclude, sourceProjectConfiguration === null || sourceProjectConfiguration === void 0 ? void 0 : sourceProjectConfiguration.patternsToExclude); } else { if (!packagesJson) { return false; } const dependenciesConfigurations = dependencyConfigurationsByName.get(packagesJson.name); if (!dependenciesConfigurations) { return false; } const matchedDependenciesConfigurations = dependenciesConfigurations.filter((d) => semver_1.default.satisfies(packagesJson.version, d.dependencyVersionRange)); return matchedDependenciesConfigurations.some((d) => excludeFileByPatterns(d.patternsToInclude, d.patternsToExclude)); } }; if (sourceProjectConfiguration && !includeNpmIgnoreFiles) { // Only use the npmignore filter if the project configuration explicitly asks for it useNpmIgnoreFilter = true; } const targetFolderPath = (0, Utils_1.remapSourcePathForTargetFolder)({ ...options, sourcePath: sourceFolderPath }); if (useNpmIgnoreFilter) { const npmPackFiles = await PackageExtractor.getPackageIncludedFilesAsync(sourceFolderPath); await node_core_library_1.Async.forEachAsync(npmPackFiles, async (npmPackFile) => { // In issue https://github.com/microsoft/rushstack/issues/2121 we found that npm-packlist sometimes returns // duplicate file paths, for example: // // 'dist//index.js' // 'dist/index.js' // // Filter out files that are excluded by the project configuration or dependency configuration. if (isFileExcluded(npmPackFile)) { return; } const sourceFilePath = path.resolve(sourceFolderPath, npmPackFile); const { kind, linkStats: sourceFileStats } = await state.symlinkAnalyzer.analyzePathAsync({ inputPath: sourceFilePath }); if (kind === 'file') { const targetFilePath = path.resolve(targetFolderPath, npmPackFile); await assetHandler.includeAssetAsync({ sourceFilePath, sourceFileStats, targetFilePath }); } }, { concurrency: constants_1.MAX_CONCURRENCY }); } else { // use a simplistic "ignore" ruleset to filter the files const ignoreFilter = (0, ignore_1.default)(); ignoreFilter.add([ // The top-level node_modules folder is always excluded '/node_modules', // Also exclude well-known folders that can contribute a lot of unnecessary files '**/.git', '**/.svn', '**/.hg', '**/.DS_Store' ]); // Do a breadth-first search of the source folder, copying each file to the target folder const queue = new node_core_library_1.AsyncQueue([sourceFolderPath]); await node_core_library_1.Async.forEachAsync(queue, async ([sourcePath, callback]) => { const relativeSourcePath = path.relative(sourceFolderPath, sourcePath); if (relativeSourcePath !== '' && ignoreFilter.ignores(relativeSourcePath)) { callback(); return; } const sourcePathNode = await state.symlinkAnalyzer.analyzePathAsync({ inputPath: sourcePath, // Treat all links to external paths as if they are files for this scenario. In the future, we may // want to explore the target of the external link to see if all files within the target are // excluded, and throw if they are not. shouldIgnoreExternalLink: (linkSourcePath) => { // Ignore the provided linkSourcePath since it may not be the first link in the chain. Instead, // we will consider only the relativeSourcePath, since that would be our entrypoint into the // link chain. return isFileExcluded(relativeSourcePath); } }); if (sourcePathNode === undefined) { // The target was a symlink that is excluded. We don't need to do anything. callback(); return; } else if (sourcePathNode.kind === 'file') { // Only ignore files and not folders to ensure that we traverse the contents of all folders. This is // done so that we can match against subfolder patterns, ex. "src/subfolder/**/*" if (relativeSourcePath !== '' && isFileExcluded(relativeSourcePath)) { callback(); return; } const targetFilePath = path.resolve(targetFolderPath, relativeSourcePath); await assetHandler.includeAssetAsync({ sourceFilePath: sourcePath, sourceFileStats: sourcePathNode.linkStats, targetFilePath }); } else if (sourcePathNode.kind === 'folder') { const children = await node_core_library_1.FileSystem.readFolderItemNamesAsync(sourcePath); for (const child of children) { queue.push(path.join(sourcePath, child)); } } callback(); }, { concurrency: constants_1.MAX_CONCURRENCY }); } } /** * Write the common/deploy/deploy-metadata.json file. */ async _writeExtractorMetadataAsync(options, state) { const { mainProjectName, sourceRootFolder, targetRootFolder, linkCreation, linkCreationScriptPath } = options; const { projectConfigurationsByPath } = state; const extractorMetadataFolderPath = linkCreation === 'script' && linkCreationScriptPath ? path.dirname(path.resolve(targetRootFolder, linkCreationScriptPath)) : targetRootFolder; const extractorMetadataFilePath = path.join(extractorMetadataFolderPath, PathConstants_1.EXTRACTOR_METADATA_FILENAME); const extractorMetadataJson = { mainProjectName, projects: [], links: [], files: [] }; for (const { projectFolder, projectName } of projectConfigurationsByPath.values()) { if (state.foldersToCopy.has(projectFolder)) { extractorMetadataJson.projects.push({ projectName, path: (0, Utils_1.remapPathForExtractorMetadata)(sourceRootFolder, projectFolder) }); } } // Remap the links to be relative to target folder for (const { kind, linkPath, targetPath } of state.symlinkAnalyzer.reportSymlinks()) { extractorMetadataJson.links.push({ kind, linkPath: (0, Utils_1.remapPathForExtractorMetadata)(sourceRootFolder, linkPath), targetPath: (0, Utils_1.remapPathForExtractorMetadata)(sourceRootFolder, targetPath) }); } for (const assetPath of state.assetHandler.assetPaths) { extractorMetadataJson.files.push((0, Utils_1.remapPathForExtractorMetadata)(targetRootFolder, assetPath)); } const extractorMetadataFileContent = JSON.stringify(extractorMetadataJson, undefined, 0); await state.assetHandler.includeAssetAsync({ sourceFileContent: extractorMetadataFileContent, targetFilePath: extractorMetadataFilePath }); } async _makeBinLinksAsync(options, state) { const { terminal } = options; const extractedProjectFolderPaths = []; for (const folderPath of state.projectConfigurationsByPath.keys()) { if (state.foldersToCopy.has(folderPath)) { extractedProjectFolderPaths.push((0, Utils_1.remapSourcePathForTargetFolder)({ ...options, sourcePath: folderPath })); } } const binFilePaths = await (0, Utils_1.makeBinLinksAsync)(terminal, extractedProjectFolderPaths); await node_core_library_1.Async.forEachAsync(binFilePaths, (targetFilePath) => state.assetHandler.includeAssetAsync({ targetFilePath }), { concurrency: constants_1.MAX_CONCURRENCY }); } async _writeCreateLinksScriptAsync(options, state) { const { terminal, targetRootFolder, linkCreationScriptPath } = options; const { assetHandler } = state; terminal.writeLine(`Creating ${PathConstants_1.CREATE_LINKS_SCRIPT_FILENAME}`); const createLinksSourceFilePath = `${PathConstants_1.SCRIPTS_FOLDER_PATH}/${PathConstants_1.CREATE_LINKS_SCRIPT_FILENAME}`; const createLinksTargetFilePath = path.resolve(targetRootFolder, linkCreationScriptPath || PathConstants_1.CREATE_LINKS_SCRIPT_FILENAME); let createLinksScriptContent = await node_core_library_1.FileSystem.readFileAsync(createLinksSourceFilePath); createLinksScriptContent = createLinksScriptContent.replace(exports.TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING, node_core_library_1.Path.convertToSlashes(path.relative(path.dirname(createLinksTargetFilePath), targetRootFolder))); await assetHandler.includeAssetAsync({ sourceFileContent: createLinksScriptContent, targetFilePath: createLinksTargetFilePath }); } } exports.PackageExtractor = PackageExtractor; //# sourceMappingURL=PackageExtractor.js.map