diff --git a/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/main_2025-08-04-19-18.json b/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/main_2025-08-04-19-18.json new file mode 100644 index 00000000000..03cf6c84643 --- /dev/null +++ b/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/main_2025-08-04-19-18.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin", + "comment": "Manually process wildcard directories for watching instead of watching all read directories.", + "type": "patch" + } + ], + "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin" +} \ No newline at end of file diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts index c1921dd1526..4d281846216 100644 --- a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { Dirent, Stats } from 'node:fs'; +import type { Dirent } from 'node:fs'; import path from 'node:path'; import { type ChildProcess, fork } from 'node:child_process'; @@ -159,68 +159,6 @@ async function transpileProjectAsync( }); const { ts } = tool; - if (getWatchFs) { - const watchFs: IWatchFileSystem = getWatchFs(); - const { system } = tool; - const emptyFileSystemEntries: { files: string[]; directories: string[] } = { files: [], directories: [] }; - // Copied from TypeScript, but using the watch file system - function getAccessibleFileSystemEntries(directory: string): { files: string[]; directories: string[] } { - try { - const entries: Dirent[] = watchFs.readdirSync(directory || '.', { withFileTypes: true }); - const files: string[] = []; - const directories: string[] = []; - for (const dirent of entries) { - const entry: string = dirent.name; - if (entry === '.' || entry === '..') { - continue; - } - let stat: Stats | Dirent | undefined; - if (dirent.isSymbolicLink()) { - const name: string = ts.combinePaths(directory, entry); - stat = watchFs.statSync(name); - if (!stat) { - continue; - } - } else { - stat = dirent; - } - - if (stat.isFile()) { - files.push(entry); - } else if (stat.isDirectory()) { - directories.push(entry); - } - } - files.sort(); - directories.sort(); - return { files, directories }; - } catch { - return emptyFileSystemEntries; - } - } - - system.readDirectory = ( - dirPath: string, - extensions?: string[], - excludes?: string[], - includes?: string[], - depth?: number - ): string[] => { - return ts.matchFiles( - dirPath, - extensions, - excludes, - includes, - system.useCaseSensitiveFileNames, - buildFolderPath, - depth, - getAccessibleFileSystemEntries, - system.realpath!, - system.directoryExists - ); - }; - } - const tsconfigPath: string = getTsconfigFilePath(heftConfiguration, pluginOptions.tsConfigPath); const parsedTsConfig: TTypeScript.ParsedCommandLine | undefined = loadTsconfig({ tool, tsconfigPath }); @@ -229,6 +167,36 @@ async function transpileProjectAsync( return; } + if (getWatchFs && parsedTsConfig.wildcardDirectories) { + // If the tsconfig has wildcard directories, we need to ensure that they are watched for file changes. + const directoryQueue: Map = new Map(); + for (const [wildcardDirectory, type] of Object.entries(parsedTsConfig.wildcardDirectories)) { + directoryQueue.set(path.normalize(wildcardDirectory), type === ts.WatchDirectoryFlags.Recursive); + } + + if (directoryQueue.size > 0) { + const watchFs: IWatchFileSystem = getWatchFs(); + + for (const [wildcardDirectory, isRecursive] of directoryQueue) { + const dirents: Dirent[] = watchFs.readdirSync(wildcardDirectory, { + withFileTypes: true + }); + + if (isRecursive) { + for (const dirent of dirents) { + if (dirent.isDirectory()) { + // Using path.join because we want platform-normalized paths. + const absoluteDirentPath: string = path.join(wildcardDirectory, dirent.name); + directoryQueue.set(absoluteDirentPath, true); + } + } + } + } + + logger.terminal.writeDebugLine(`Watching for changes in ${directoryQueue.size} directories`); + } + } + if (emitKinds.length < 1) { throw new Error( 'One or more emit kinds must be specified in the plugin options. To disable SWC transpilation, ' +