Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build-tests/heft-swc-test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"license": "MIT",
"scripts": {
"build": "heft build --clean",
"build-watch": "heft build-watch --clean",
"_phase:build": "heft run --only build -- --clean",
"_phase:test": "heft run --only test -- --clean"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/heft-isolated-typescript-transpile-plugin",
"comment": "Add support for watch mode.",
"type": "minor"
}
],
"packageName": "@rushstack/heft-isolated-typescript-transpile-plugin"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/heft-typescript-plugin",
"comment": "Update internal typings.",
"type": "patch"
}
],
"packageName": "@rushstack/heft-typescript-plugin"
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
// 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 path from 'node:path';
import { type ChildProcess, fork } from 'node:child_process';

import { Path } from '@rushstack/node-core-library';
import type { HeftConfiguration, IHeftTaskPlugin, IHeftTaskSession, IScopedLogger } from '@rushstack/heft';
import { Async, Path } from '@rushstack/node-core-library';
import type {
HeftConfiguration,
IHeftTaskPlugin,
IHeftTaskSession,
IScopedLogger,
IWatchFileSystem,
IWatchedFileState
} from '@rushstack/heft';
import { LookupByPath } from '@rushstack/lookup-by-path';
import {
_loadTypeScriptToolAsync as loadTypeScriptToolAsync,
Expand Down Expand Up @@ -120,14 +128,27 @@ export default class SwcIsolatedTranspilePlugin implements IHeftTaskPlugin<ISwcI

await transpileProjectAsync(heftConfiguration, pluginOptions, logger, this.accessor);
});

heftSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, async (incrementalOptions) => {
const { logger } = heftSession;

await transpileProjectAsync(
heftConfiguration,
pluginOptions,
logger,
this.accessor,
() => incrementalOptions.watchFs
);
});
}
}

async function transpileProjectAsync(
heftConfiguration: HeftConfiguration,
pluginOptions: ISwcIsolatedTranspileOptions,
logger: IScopedLogger,
{ hooks: { getSwcOptions: getSwcOptionsHook } }: ISwcIsolatedTranspilePluginAccessor
{ hooks: { getSwcOptions: getSwcOptionsHook } }: ISwcIsolatedTranspilePluginAccessor,
getWatchFs?: (() => IWatchFileSystem) | undefined
): Promise<void> {
const { buildFolderPath } = heftConfiguration;
const { emitKinds = [] } = pluginOptions;
Expand All @@ -138,6 +159,68 @@ 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 });

Expand Down Expand Up @@ -170,6 +253,29 @@ async function transpileProjectAsync(
}

const sourceFilePaths: string[] = filesFromTsConfig.filter((filePath) => !filePath.endsWith('.d.ts'));
const changedFilePaths: string[] = getWatchFs ? [] : sourceFilePaths;
if (getWatchFs) {
const watchFs: IWatchFileSystem = getWatchFs();
await Async.forEachAsync(
sourceFilePaths,
async (file: string) => {
const fileState: IWatchedFileState = await watchFs.getStateAndTrackAsync(path.normalize(file));
if (fileState.changed) {
changedFilePaths.push(file);
}
},
{
concurrency: 4
}
);
}

if (changedFilePaths.length < 1) {
logger.terminal.writeLine('No changed files found. Skipping transpile.');
return;
}

changedFilePaths.sort();

logger.terminal.writeVerboseLine('Reading Config');

Expand Down Expand Up @@ -289,7 +395,7 @@ async function transpileProjectAsync(
};

const indexForOptions: Map<string, number> = new Map();
for (const srcFilePath of sourceFilePaths) {
for (const srcFilePath of changedFilePaths) {
const rootPrefixLength: number | undefined = rootDirsPaths.findChildPath(srcFilePath);

if (rootPrefixLength === undefined) {
Expand Down Expand Up @@ -327,7 +433,7 @@ async function transpileProjectAsync(
}
}

logger.terminal.writeLine(`Transpiling ${tasks.length} files...`);
logger.terminal.writeLine(`Transpiling ${changedFilePaths.length} changed source files...`);

const result: IWorkerResult = await new Promise((resolve, reject) => {
const workerPath: string = require.resolve('./TranspileWorker.js');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export interface IExtendedTypeScript {
system?: TTypescript.System
): TTypescript.CompilerHost;

combinePaths(path1: string, path2: string): string;

/**
* https://github.com/microsoft/TypeScript/blob/782c09d783e006a697b4ba6d1e7ec2f718ce8393/src/compiler/utilities.ts#L6540
*/
Expand Down
Loading