@rushstack/package-extractor
Version:
A library for bundling selected files and dependencies into a deployable package.
568 lines • 32.2 kB
JavaScript
// 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
;