@rushstack/heft
Advanced tools
Comparing version 0.57.1 to 0.58.0
@@ -9,2 +9,4 @@ /** | ||
/// <reference types="node" /> | ||
import { AsyncParallelHook } from 'tapable'; | ||
@@ -254,3 +256,3 @@ import { AsyncSeriesWaterfallHook } from 'tapable'; | ||
*/ | ||
sourcePath: string; | ||
sourcePath?: string; | ||
/** | ||
@@ -257,0 +259,0 @@ * File extensions that should be included from the source folder. Only supported when the sourcePath |
@@ -8,5 +8,5 @@ // This file is read by tools that parse documentation comments conforming to the TSDoc standard. | ||
"packageName": "@microsoft/api-extractor", | ||
"packageVersion": "7.36.2" | ||
"packageVersion": "7.36.3" | ||
} | ||
] | ||
} |
@@ -67,3 +67,3 @@ "use strict"; | ||
// Delete the files | ||
if (deleteOperations.length > 0) { | ||
if (deleteOperations.length) { | ||
const rootFolderPath = this._internalHeftSession.heftConfiguration.buildFolderPath; | ||
@@ -70,0 +70,0 @@ await (0, DeleteFilesPlugin_1.deleteFilesAsync)(rootFolderPath, deleteOperations, this._terminal); |
@@ -52,2 +52,3 @@ "use strict"; | ||
logger.resetErrorsAndWarnings(); | ||
const rootFolderPath = this._options.internalHeftSession.heftConfiguration.buildFolderPath; | ||
const isWatchMode = taskSession.parameters.watch && !!requestRun; | ||
@@ -64,5 +65,5 @@ const { terminal } = logger; | ||
}); | ||
// Do this here so that we only need to do it once for each run | ||
for (const copyOperation of fileOperations.copyOperations) { | ||
// Consolidate fileExtensions, includeGlobs, excludeGlobs | ||
(0, FileGlobSpecifier_1.normalizeFileSelectionSpecifier)(copyOperation); | ||
(0, CopyFilesPlugin_1.normalizeCopyOperation)(rootFolderPath, copyOperation); | ||
} | ||
@@ -121,7 +122,6 @@ this._fileOperations = fileOperations; | ||
if (this._fileOperations) { | ||
const rootFolderPath = this._options.internalHeftSession.heftConfiguration.buildFolderPath; | ||
const { copyOperations, deleteOperations } = this._fileOperations; | ||
await Promise.all([ | ||
copyOperations.size > 0 | ||
? (0, CopyFilesPlugin_1.copyFilesAsync)(rootFolderPath, copyOperations, logger.terminal, isWatchMode ? getWatchFileSystemAdapter() : undefined) | ||
? (0, CopyFilesPlugin_1.copyFilesAsync)(copyOperations, logger.terminal, isWatchMode ? getWatchFileSystemAdapter() : undefined) | ||
: Promise.resolve(), | ||
@@ -128,0 +128,0 @@ deleteOperations.size > 0 |
@@ -51,3 +51,4 @@ import { ITerminal } from '@rushstack/node-core-library'; | ||
} | ||
export declare function copyFilesAsync(rootFolderPath: string, copyOperations: Iterable<ICopyOperation>, terminal: ITerminal, watchFileSystemAdapter?: WatchFileSystemAdapter): Promise<void>; | ||
export declare function normalizeCopyOperation(rootFolderPath: string, copyOperation: ICopyOperation): void; | ||
export declare function copyFilesAsync(copyOperations: Iterable<ICopyOperation>, terminal: ITerminal, watchFileSystemAdapter?: WatchFileSystemAdapter): Promise<void>; | ||
export default class CopyFilesPlugin implements IHeftTaskPlugin<ICopyFilesPluginOptions> { | ||
@@ -54,0 +55,0 @@ apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, pluginOptions: ICopyFilesPluginOptions): void; |
@@ -28,3 +28,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.copyFilesAsync = void 0; | ||
exports.copyFilesAsync = exports.normalizeCopyOperation = void 0; | ||
const path = __importStar(require("path")); | ||
@@ -34,8 +34,13 @@ const node_core_library_1 = require("@rushstack/node-core-library"); | ||
const FileGlobSpecifier_1 = require("./FileGlobSpecifier"); | ||
async function copyFilesAsync(rootFolderPath, copyOperations, terminal, watchFileSystemAdapter) { | ||
const copyDescriptorByDestination = await _getCopyDescriptorsAsync(rootFolderPath, copyOperations, watchFileSystemAdapter); | ||
function normalizeCopyOperation(rootFolderPath, copyOperation) { | ||
(0, FileGlobSpecifier_1.normalizeFileSelectionSpecifier)(rootFolderPath, copyOperation); | ||
copyOperation.destinationFolders = copyOperation.destinationFolders.map((x) => path.resolve(rootFolderPath, x)); | ||
} | ||
exports.normalizeCopyOperation = normalizeCopyOperation; | ||
async function copyFilesAsync(copyOperations, terminal, watchFileSystemAdapter) { | ||
const copyDescriptorByDestination = await _getCopyDescriptorsAsync(copyOperations, watchFileSystemAdapter); | ||
await _copyFilesInnerAsync(copyDescriptorByDestination, terminal); | ||
} | ||
exports.copyFilesAsync = copyFilesAsync; | ||
async function _getCopyDescriptorsAsync(rootFolderPath, copyConfigurations, fs) { | ||
async function _getCopyDescriptorsAsync(copyConfigurations, fileSystemAdapter) { | ||
// Create a map to deduplicate and prevent double-writes | ||
@@ -46,9 +51,12 @@ // resolvedDestinationFilePath -> descriptor | ||
// "sourcePath" is required to be a folder. To copy a single file, put the parent folder in "sourcePath" | ||
// and the filename in "includeGlobs" | ||
copyConfiguration.sourcePath = path.resolve(rootFolderPath, copyConfiguration.sourcePath); | ||
// and the filename in "includeGlobs". | ||
const sourceFolder = copyConfiguration.sourcePath; | ||
const sourceFilePaths = await (0, FileGlobSpecifier_1.getFilePathsAsync)(copyConfiguration, fs); | ||
const sourceFiles = await (0, FileGlobSpecifier_1.getFileSelectionSpecifierPathsAsync)({ | ||
fileGlobSpecifier: copyConfiguration, | ||
fileSystemAdapter | ||
}); | ||
// Dedupe and throw if a double-write is detected | ||
for (const destinationFolderPath of copyConfiguration.destinationFolders) { | ||
for (const sourceFilePath of sourceFilePaths) { | ||
// We only need to care about the keys of the map since we know all the keys are paths to files | ||
for (const sourceFilePath of sourceFiles.keys()) { | ||
// Only include the relative path from the sourceFolder if flatten is false | ||
@@ -110,11 +118,2 @@ const resolvedDestinationPath = path.resolve(destinationFolderPath, copyConfiguration.flatten | ||
} | ||
function* _resolveCopyOperationPaths(heftConfiguration, copyOperations) { | ||
const { buildFolderPath } = heftConfiguration; | ||
function resolvePath(inputPath) { | ||
return inputPath ? path.resolve(buildFolderPath, inputPath) : buildFolderPath; | ||
} | ||
for (const copyOperation of copyOperations) { | ||
yield Object.assign(Object.assign({}, copyOperation), { sourcePath: resolvePath(copyOperation.sourcePath), destinationFolders: copyOperation.destinationFolders.map(resolvePath) }); | ||
} | ||
} | ||
const PLUGIN_NAME = 'copy-files-plugin'; | ||
@@ -124,3 +123,3 @@ class CopyFilesPlugin { | ||
taskSession.hooks.registerFileOperations.tap(PLUGIN_NAME, (operations) => { | ||
for (const operation of _resolveCopyOperationPaths(heftConfiguration, pluginOptions.copyOperations)) { | ||
for (const operation of pluginOptions.copyOperations) { | ||
operations.copyOperations.add(operation); | ||
@@ -127,0 +126,0 @@ } |
"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 (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.deleteFilesAsync = void 0; | ||
const path = __importStar(require("path")); | ||
const node_core_library_1 = require("@rushstack/node-core-library"); | ||
@@ -34,22 +10,26 @@ const Constants_1 = require("../utilities/Constants"); | ||
async function _getPathsToDeleteAsync(rootFolderPath, deleteOperations) { | ||
const pathsToDelete = new Set(); | ||
const result = { | ||
filesToDelete: new Set(), | ||
foldersToDelete: new Set() | ||
}; | ||
await node_core_library_1.Async.forEachAsync(deleteOperations, async (deleteOperation) => { | ||
var _a, _b, _c; | ||
deleteOperation.sourcePath = path.resolve(rootFolderPath, deleteOperation.sourcePath); | ||
if (!((_a = deleteOperation.fileExtensions) === null || _a === void 0 ? void 0 : _a.length) && | ||
!((_b = deleteOperation.includeGlobs) === null || _b === void 0 ? void 0 : _b.length) && | ||
!((_c = deleteOperation.excludeGlobs) === null || _c === void 0 ? void 0 : _c.length)) { | ||
// If no globs or file extensions are provided add the path to the set of paths to delete | ||
pathsToDelete.add(deleteOperation.sourcePath); | ||
} | ||
else { | ||
(0, FileGlobSpecifier_1.normalizeFileSelectionSpecifier)(deleteOperation); | ||
// Glob the files under the source path and add them to the set of files to delete | ||
const sourceFilePaths = await (0, FileGlobSpecifier_1.getFilePathsAsync)(deleteOperation); | ||
for (const sourceFilePath of sourceFilePaths) { | ||
pathsToDelete.add(sourceFilePath); | ||
(0, FileGlobSpecifier_1.normalizeFileSelectionSpecifier)(rootFolderPath, deleteOperation); | ||
// Glob the files under the source path and add them to the set of files to delete | ||
const sourcePaths = await (0, FileGlobSpecifier_1.getFileSelectionSpecifierPathsAsync)({ | ||
fileGlobSpecifier: deleteOperation, | ||
includeFolders: true | ||
}); | ||
for (const [sourcePath, dirent] of sourcePaths) { | ||
// If the sourcePath is a folder, add it to the foldersToDelete set. Otherwise, add it to | ||
// the filesToDelete set. Symlinks and junctions are treated as files, and thus will fall | ||
// into the filesToDelete set. | ||
if (dirent.isDirectory()) { | ||
result.foldersToDelete.add(sourcePath); | ||
} | ||
else { | ||
result.filesToDelete.add(sourcePath); | ||
} | ||
} | ||
}, { concurrency: Constants_1.Constants.maxParallelism }); | ||
return pathsToDelete; | ||
return result; | ||
} | ||
@@ -64,3 +44,4 @@ async function deleteFilesAsync(rootFolderPath, deleteOperations, terminal) { | ||
let deletedFolders = 0; | ||
await node_core_library_1.Async.forEachAsync(pathsToDelete, async (pathToDelete) => { | ||
const { filesToDelete, foldersToDelete } = pathsToDelete; | ||
await node_core_library_1.Async.forEachAsync(filesToDelete, async (pathToDelete) => { | ||
try { | ||
@@ -74,16 +55,32 @@ await node_core_library_1.FileSystem.deleteFileAsync(pathToDelete, { throwIfNotExists: true }); | ||
if (!node_core_library_1.FileSystem.isNotExistError(error)) { | ||
// When we encounter an error relating to deleting a directory as if it was a file, | ||
// attempt to delete the folder. Windows throws the unlink not permitted error, while | ||
// linux throws the EISDIR error. | ||
if (node_core_library_1.FileSystem.isUnlinkNotPermittedError(error) || node_core_library_1.FileSystem.isDirectoryError(error)) { | ||
await node_core_library_1.FileSystem.deleteFolderAsync(pathToDelete); | ||
terminal.writeVerboseLine(`Deleted folder "${pathToDelete}".`); | ||
deletedFolders++; | ||
} | ||
else { | ||
throw error; | ||
} | ||
throw error; | ||
} | ||
} | ||
}, { concurrency: Constants_1.Constants.maxParallelism }); | ||
// Reverse the list of matching folders. Assuming that the list of folders came from | ||
// the globber, the folders will be specified in tree-walk order, so by reversing the | ||
// list we delete the deepest folders first and avoid not-exist errors for subfolders | ||
// of an already-deleted parent folder. | ||
const reversedFoldersToDelete = Array.from(foldersToDelete).reverse(); | ||
// Clear out any folders that were encountered during the file deletion process. This | ||
// will recursively delete the folder and it's contents. There are two scenarios that | ||
// this handles: | ||
// - Deletions of empty folder structures (ex. when the delete glob is '**/*') | ||
// - Deletions of folders that still contain files (ex. when the delete glob is 'lib') | ||
// In the latter scenario, the count of deleted files will not be tracked. However, | ||
// this is a fair trade-off for the performance benefit of not having to glob the | ||
// folder structure again. | ||
await node_core_library_1.Async.forEachAsync(reversedFoldersToDelete, async (folderToDelete) => { | ||
try { | ||
await node_core_library_1.FileSystem.deleteFolderAsync(folderToDelete); | ||
terminal.writeVerboseLine(`Deleted folder "${folderToDelete}".`); | ||
deletedFolders++; | ||
} | ||
catch (error) { | ||
// If it doesn't exist, we can ignore the error. | ||
if (!node_core_library_1.FileSystem.isNotExistError(error)) { | ||
throw error; | ||
} | ||
} | ||
}, { concurrency: Constants_1.Constants.maxParallelism }); | ||
if (deletedFiles > 0 || deletedFolders > 0) { | ||
@@ -94,9 +91,2 @@ terminal.writeLine(`Deleted ${deletedFiles} file${deletedFiles !== 1 ? 's' : ''} ` + | ||
} | ||
function* _resolveDeleteOperationPaths(heftConfiguration, deleteOperations) { | ||
const { buildFolderPath } = heftConfiguration; | ||
for (const deleteOperation of deleteOperations) { | ||
const { sourcePath } = deleteOperation; | ||
yield Object.assign(Object.assign({}, deleteOperation), { sourcePath: sourcePath ? path.resolve(buildFolderPath, sourcePath) : buildFolderPath }); | ||
} | ||
} | ||
const PLUGIN_NAME = 'delete-files-plugin'; | ||
@@ -106,3 +96,3 @@ class DeleteFilesPlugin { | ||
taskSession.hooks.registerFileOperations.tap(PLUGIN_NAME, (fileOperations) => { | ||
for (const deleteOperation of _resolveDeleteOperationPaths(heftConfiguration, pluginOptions.deleteOperations)) { | ||
for (const deleteOperation of pluginOptions.deleteOperations) { | ||
fileOperations.deleteOperations.add(deleteOperation); | ||
@@ -109,0 +99,0 @@ } |
@@ -1,2 +0,4 @@ | ||
import { FileSystemAdapter } from 'fast-glob'; | ||
/// <reference types="node" /> | ||
import type * as fs from 'fs'; | ||
import { type FileSystemAdapter } from 'fast-glob'; | ||
import type { IWatchFileSystemAdapter, IWatchedFileState } from '../utilities/WatchFileSystemAdapter'; | ||
@@ -14,3 +16,3 @@ /** | ||
*/ | ||
sourcePath: string; | ||
sourcePath?: string; | ||
/** | ||
@@ -59,2 +61,7 @@ * File extensions that should be included from the source folder. Only supported when the sourcePath | ||
} | ||
export interface IGetFileSelectionSpecifierPathsOptions { | ||
fileGlobSpecifier: IFileSelectionSpecifier; | ||
includeFolders?: boolean; | ||
fileSystemAdapter?: FileSystemAdapter; | ||
} | ||
/** | ||
@@ -82,4 +89,4 @@ * Glob a set of files and return a list of paths that match the provided patterns. | ||
export declare function watchGlobAsync(pattern: string | string[], options: IWatchGlobOptions): Promise<Map<string, IWatchedFileState>>; | ||
export declare function getFilePathsAsync(fileGlobSpecifier: IFileSelectionSpecifier, fs?: FileSystemAdapter): Promise<Set<string>>; | ||
export declare function normalizeFileSelectionSpecifier(fileGlobSpecifier: IFileSelectionSpecifier): void; | ||
export declare function getFileSelectionSpecifierPathsAsync(options: IGetFileSelectionSpecifierPathsOptions): Promise<Map<string, fs.Dirent>>; | ||
export declare function normalizeFileSelectionSpecifier(rootPath: string, fileGlobSpecifier: IFileSelectionSpecifier): void; | ||
//# sourceMappingURL=FileGlobSpecifier.d.ts.map |
@@ -31,3 +31,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.normalizeFileSelectionSpecifier = exports.getFilePathsAsync = exports.watchGlobAsync = void 0; | ||
exports.normalizeFileSelectionSpecifier = exports.getFileSelectionSpecifierPathsAsync = exports.watchGlobAsync = void 0; | ||
const path = __importStar(require("path")); | ||
@@ -55,16 +55,24 @@ const fast_glob_1 = __importDefault(require("fast-glob")); | ||
exports.watchGlobAsync = watchGlobAsync; | ||
async function getFilePathsAsync(fileGlobSpecifier, fs) { | ||
const rawFiles = await (0, fast_glob_1.default)(fileGlobSpecifier.includeGlobs, { | ||
fs, | ||
async function getFileSelectionSpecifierPathsAsync(options) { | ||
const { fileGlobSpecifier, includeFolders, fileSystemAdapter } = options; | ||
const rawEntries = await (0, fast_glob_1.default)(fileGlobSpecifier.includeGlobs, { | ||
fs: fileSystemAdapter, | ||
cwd: fileGlobSpecifier.sourcePath, | ||
ignore: fileGlobSpecifier.excludeGlobs, | ||
onlyFiles: !includeFolders, | ||
dot: true, | ||
absolute: true | ||
absolute: true, | ||
objectMode: true | ||
}); | ||
if (fs && isWatchFileSystemAdapter(fs)) { | ||
const changedFiles = new Set(); | ||
await node_core_library_1.Async.forEachAsync(rawFiles, async (file) => { | ||
const state = await fs.getStateAndTrackAsync(path.normalize(file)); | ||
let results; | ||
if (fileSystemAdapter && isWatchFileSystemAdapter(fileSystemAdapter)) { | ||
results = new Map(); | ||
await node_core_library_1.Async.forEachAsync(rawEntries, async (entry) => { | ||
const { path: filePath, dirent } = entry; | ||
if (entry.dirent.isDirectory()) { | ||
return; | ||
} | ||
const state = await fileSystemAdapter.getStateAndTrackAsync(path.normalize(filePath)); | ||
if (state.changed) { | ||
changedFiles.add(file); | ||
results.set(filePath, dirent); | ||
} | ||
@@ -74,8 +82,12 @@ }, { | ||
}); | ||
return changedFiles; | ||
} | ||
return new Set(rawFiles); | ||
else { | ||
results = new Map(rawEntries.map((entry) => [entry.path, entry.dirent])); | ||
} | ||
return results; | ||
} | ||
exports.getFilePathsAsync = getFilePathsAsync; | ||
function normalizeFileSelectionSpecifier(fileGlobSpecifier) { | ||
exports.getFileSelectionSpecifierPathsAsync = getFileSelectionSpecifierPathsAsync; | ||
function normalizeFileSelectionSpecifier(rootPath, fileGlobSpecifier) { | ||
const { sourcePath } = fileGlobSpecifier; | ||
fileGlobSpecifier.sourcePath = sourcePath ? path.resolve(rootPath, sourcePath) : rootPath; | ||
fileGlobSpecifier.includeGlobs = getIncludedGlobPatterns(fileGlobSpecifier); | ||
@@ -82,0 +94,0 @@ } |
@@ -18,16 +18,9 @@ { | ||
"additionalProperties": false, | ||
"required": ["destinationFolders"], | ||
"anyOf": [ | ||
{ | ||
"required": ["sourcePath"] | ||
}, | ||
{ | ||
"required": ["includeGlobs"] | ||
}, | ||
{ | ||
"required": ["fileExtensions"] | ||
} | ||
{ "required": ["sourcePath"] }, | ||
{ "required": ["fileExtensions"] }, | ||
{ "required": ["includeGlobs"] }, | ||
{ "required": ["excludeGlobs"] } | ||
], | ||
"required": ["destinationFolders"], | ||
"properties": { | ||
@@ -34,0 +27,0 @@ "sourcePath": { |
@@ -19,13 +19,7 @@ { | ||
"anyOf": [ | ||
{ | ||
"required": ["sourcePath"] | ||
}, | ||
{ | ||
"required": ["includeGlobs"] | ||
}, | ||
{ | ||
"required": ["fileExtensions"] | ||
} | ||
{ "required": ["sourcePath"] }, | ||
{ "required": ["fileExtensions"] }, | ||
{ "required": ["includeGlobs"] }, | ||
{ "required": ["excludeGlobs"] } | ||
], | ||
"properties": { | ||
@@ -32,0 +26,0 @@ "sourcePath": { |
@@ -57,2 +57,8 @@ { | ||
"additionalProperties": false, | ||
"anyOf": [ | ||
{ "required": ["sourcePath"] }, | ||
{ "required": ["fileExtensions"] }, | ||
{ "required": ["includeGlobs"] }, | ||
{ "required": ["excludeGlobs"] } | ||
], | ||
"properties": { | ||
@@ -59,0 +65,0 @@ "sourcePath": { |
{ | ||
"name": "@rushstack/heft", | ||
"version": "0.57.1", | ||
"version": "0.58.0", | ||
"description": "Build all your JavaScript projects the same way: A way that works.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
994638
10230