From cd46799bf1ca26244e606cea53928679949e8e8c Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Wed, 28 May 2025 17:49:50 -0700 Subject: [PATCH 1/8] [heft-lint] Adds support for using lint plugin without typescript phase --- .../dist/tsdoc-metadata.json | 2 +- ...nt-plugin-standalone_2025-05-29-00-49.json | 10 +++++ .../build-tests-subspace/pnpm-lock.yaml | 25 ++++++----- .../build-tests-subspace/repo-state.json | 4 +- .../config/subspaces/default/pnpm-lock.yaml | 6 +-- heft-plugins/heft-lint-plugin/package.json | 6 +-- .../heft-lint-plugin/src/LintPlugin.ts | 45 +++++++++++++++++++ 7 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json diff --git a/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json b/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json index 5c31157a604..89625ac60e4 100644 --- a/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json +++ b/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json @@ -5,7 +5,7 @@ "toolPackages": [ { "packageName": "@microsoft/api-extractor", - "packageVersion": "7.52.4" + "packageVersion": "7.52.8" } ] } diff --git a/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json b/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json new file mode 100644 index 00000000000..929c6f6d781 --- /dev/null +++ b/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/heft-lint-plugin", + "comment": "Adds support for using heft-lint-plugin standalone without a typescript phase", + "type": "patch" + } + ], + "packageName": "@rushstack/heft-lint-plugin" +} \ No newline at end of file diff --git a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml index 52e24abbcc7..90b7295e84d 100644 --- a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml +++ b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml @@ -114,10 +114,10 @@ importers: version: file:../../../apps/heft(@types/node@20.17.19) '@rushstack/heft-lint-plugin': specifier: file:../../heft-plugins/heft-lint-plugin - version: file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19) + version: file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19) '@rushstack/heft-typescript-plugin': specifier: file:../../heft-plugins/heft-typescript-plugin - version: file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19) + version: file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19) eslint: specifier: ~8.57.0 version: 8.57.1 @@ -6238,7 +6238,7 @@ packages: - typescript dev: true - file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19): + file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-api-extractor-plugin, type: directory} id: file:../../../heft-plugins/heft-api-extractor-plugin name: '@rushstack/heft-api-extractor-plugin' @@ -6252,7 +6252,7 @@ packages: - '@types/node' dev: true - file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19)(jest-environment-node@29.5.0): + file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19)(jest-environment-node@29.5.0): resolution: {directory: ../../../heft-plugins/heft-jest-plugin, type: directory} id: file:../../../heft-plugins/heft-jest-plugin name: '@rushstack/heft-jest-plugin' @@ -6287,7 +6287,7 @@ packages: - ts-node dev: true - file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19): + file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-lint-plugin, type: directory} id: file:../../../heft-plugins/heft-lint-plugin name: '@rushstack/heft-lint-plugin' @@ -6297,11 +6297,12 @@ packages: '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) semver: 7.5.4 + typescript: 5.8.2 transitivePeerDependencies: - '@types/node' dev: true - file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19): + file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-typescript-plugin, type: directory} id: file:../../../heft-plugins/heft-typescript-plugin name: '@rushstack/heft-typescript-plugin' @@ -6526,7 +6527,7 @@ packages: transitivePeerDependencies: - '@types/node' - file:../../../rigs/heft-node-rig(@rushstack/heft@0.73.2)(@types/node@20.17.19): + file:../../../rigs/heft-node-rig(@rushstack/heft@0.73.6)(@types/node@20.17.19): resolution: {directory: ../../../rigs/heft-node-rig, type: directory} id: file:../../../rigs/heft-node-rig name: '@rushstack/heft-node-rig' @@ -6536,10 +6537,10 @@ packages: '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) '@rushstack/eslint-config': file:../../../eslint/eslint-config(eslint@8.57.1)(typescript@5.8.2) '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) - '@rushstack/heft-api-extractor-plugin': file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19) - '@rushstack/heft-jest-plugin': file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19)(jest-environment-node@29.5.0) - '@rushstack/heft-lint-plugin': file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19) - '@rushstack/heft-typescript-plugin': file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.2)(@types/node@20.17.19) + '@rushstack/heft-api-extractor-plugin': file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19) + '@rushstack/heft-jest-plugin': file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19)(jest-environment-node@29.5.0) + '@rushstack/heft-lint-plugin': file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19) + '@rushstack/heft-typescript-plugin': file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@0.73.6)(@types/node@20.17.19) '@types/heft-jest': 1.0.1 eslint: 8.57.1 jest-environment-node: 29.5.0 @@ -6559,7 +6560,7 @@ packages: dependencies: '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) - '@rushstack/heft-node-rig': file:../../../rigs/heft-node-rig(@rushstack/heft@0.73.2)(@types/node@20.17.19) + '@rushstack/heft-node-rig': file:../../../rigs/heft-node-rig(@rushstack/heft@0.73.6)(@types/node@20.17.19) '@types/heft-jest': 1.0.1 '@types/node': 20.17.19 eslint: 8.57.1 diff --git a/common/config/subspaces/build-tests-subspace/repo-state.json b/common/config/subspaces/build-tests-subspace/repo-state.json index 72bbe58dbe0..f4d7ca8a804 100644 --- a/common/config/subspaces/build-tests-subspace/repo-state.json +++ b/common/config/subspaces/build-tests-subspace/repo-state.json @@ -1,6 +1,6 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "e47112c7d099f189f37770e10351e406cf1ec451", + "pnpmShrinkwrapHash": "02a170ed391200c1c2013b084044508d86c0cc9a", "preferredVersionsHash": "54149ea3f01558a859c96dee2052b797d4defe68", - "packageJsonInjectedDependenciesHash": "8aab06634f5544193a51484804f7d7c4fc2ad986" + "packageJsonInjectedDependenciesHash": "fe10df2247513902b2612f6730996b58d02b03a9" } diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index ecc2d923870..28d80958f7b 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -2708,6 +2708,9 @@ importers: semver: specifier: ~7.5.4 version: 7.5.4 + typescript: + specifier: ~5.8.2 + version: 5.8.2 devDependencies: '@rushstack/heft': specifier: workspace:* @@ -2733,9 +2736,6 @@ importers: tslint: specifier: ~5.20.1 version: 5.20.1(typescript@5.8.2) - typescript: - specifier: ~5.8.2 - version: 5.8.2 ../../../heft-plugins/heft-localization-typings-plugin: dependencies: diff --git a/heft-plugins/heft-lint-plugin/package.json b/heft-plugins/heft-lint-plugin/package.json index b08cfea8052..8864ff42891 100644 --- a/heft-plugins/heft-lint-plugin/package.json +++ b/heft-plugins/heft-lint-plugin/package.json @@ -19,7 +19,8 @@ }, "dependencies": { "@rushstack/node-core-library": "workspace:*", - "semver": "~7.5.4" + "semver": "~7.5.4", + "typescript": "~5.8.2" }, "devDependencies": { "@rushstack/heft-typescript-plugin": "workspace:*", @@ -29,7 +30,6 @@ "@types/semver": "7.5.0", "decoupled-local-node-rig": "workspace:*", "eslint": "~8.57.0", - "tslint": "~5.20.1", - "typescript": "~5.8.2" + "tslint": "~5.20.1" } } diff --git a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts index f20dbf30b81..e210e013f2b 100644 --- a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts +++ b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts @@ -17,10 +17,19 @@ import type { ITypeScriptPluginAccessor } from '@rushstack/heft-typescript-plugin'; +import { + readConfigFile, + parseJsonConfigFileContent, + sys, + createProgram, + type ParsedCommandLine +} from 'typescript'; + import type { LinterBase } from './LinterBase'; import { Eslint } from './Eslint'; import { Tslint } from './Tslint'; import type { IExtendedProgram, IExtendedSourceFile } from './internalTypings/TypeScriptInternals'; +import ts = require('typescript'); const PLUGIN_NAME: 'lint-plugin' = 'lint-plugin'; const TYPESCRIPT_PLUGIN_NAME: typeof TypeScriptPluginName = 'typescript-plugin'; @@ -75,6 +84,8 @@ export default class LintPlugin implements IHeftTaskPlugin { const sarifLogPath: string | undefined = relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); + // To support standalone linting, track if we have hooked to the typescript plugin + let inTypescriptPhase: boolean = false; // Use the changed files hook to kick off linting asynchronously taskSession.requestAccessToPluginByName( '@rushstack/heft-typescript-plugin', @@ -99,8 +110,42 @@ export default class LintPlugin implements IHeftTaskPlugin { this._lintingPromises.push(lintingPromise); } ); + // Set the flag to indicate that we are in the typescript phase + inTypescriptPhase = true; } ); + + if (!inTypescriptPhase) { + // Create a typescript program from the tsconfig file + const tsconfigPath: string = path.resolve(heftConfiguration.buildFolderPath, 'tsconfig.json'); + const parsed: ParsedCommandLine = parseJsonConfigFileContent( + readConfigFile(tsconfigPath, sys.readFile).config, + ts.sys, + path.dirname(tsconfigPath) + ); + const program: IExtendedProgram = createProgram({ + rootNames: parsed.fileNames, + options: parsed.options + }) as IExtendedProgram; + + // Filter out node_modules since we just want to lint the source files in the package only + const changedFiles: ReadonlySet = new Set( + program.getSourceFiles().filter((file) => !file.fileName.includes('node_modules')) + ); + const lintingPromise: Promise = this._lintAsync({ + taskSession, + heftConfiguration, + fix, + sarifLogPath, + tsProgram: program, + changedFiles: changedFiles + }); + lintingPromise.catch(() => { + // Suppress unhandled promise rejection error + }); + // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails + this._lintingPromises.push(lintingPromise); + } } let warningPrinted: boolean = false; From 429547aeb72d31e2f683ddab2aa7afd0a5c67e4f Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Thu, 29 May 2025 13:35:54 -0700 Subject: [PATCH 2/8] Address review feedback --- .../heft-lint-plugin/src/LintPlugin.ts | 141 +++++++++++------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts index e210e013f2b..a65cdaa3bc4 100644 --- a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts +++ b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts @@ -17,19 +17,12 @@ import type { ITypeScriptPluginAccessor } from '@rushstack/heft-typescript-plugin'; -import { - readConfigFile, - parseJsonConfigFileContent, - sys, - createProgram, - type ParsedCommandLine -} from 'typescript'; +import type * as TTypescript from 'typescript'; import type { LinterBase } from './LinterBase'; import { Eslint } from './Eslint'; import { Tslint } from './Tslint'; import type { IExtendedProgram, IExtendedSourceFile } from './internalTypings/TypeScriptInternals'; -import ts = require('typescript'); const PLUGIN_NAME: 'lint-plugin' = 'lint-plugin'; const TYPESCRIPT_PLUGIN_NAME: typeof TypeScriptPluginName = 'typescript-plugin'; @@ -61,31 +54,43 @@ export default class LintPlugin implements IHeftTaskPlugin { private _tslintToolPath: string | undefined; private _tslintConfigFilePath: string | undefined; + private _checkFix(taskSession: IHeftTaskSession, pluginOptions?: ILintPluginOptions): boolean { + let fix: boolean = + pluginOptions?.alwaysFix || taskSession.parameters.getFlagParameter(FIX_PARAMETER_NAME).value; + if (fix && taskSession.parameters.production) { + // Write this as a standard output message since we don't want to throw errors when running in + // production mode and "alwaysFix" is specified in the plugin options + taskSession.logger.terminal.writeLine( + 'Fix mode has been disabled since Heft is running in production mode' + ); + fix = false; + } + return fix; + } + + private _getSarifLogPath( + heftConfiguration: HeftConfiguration, + pluginOptions?: ILintPluginOptions + ): string | undefined { + const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath; + const sarifLogPath: string | undefined = + relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); + return sarifLogPath; + } + public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, pluginOptions?: ILintPluginOptions ): void { + // To support standalone linting, track if we have hooked to the typescript plugin + let inTypescriptPhase: boolean = false; + // Disable linting in watch mode. Some lint rules require the context of multiple files, which // may not be available in watch mode. if (!taskSession.parameters.watch) { - let fix: boolean = - pluginOptions?.alwaysFix || taskSession.parameters.getFlagParameter(FIX_PARAMETER_NAME).value; - if (fix && taskSession.parameters.production) { - // Write this as a standard output message since we don't want to throw errors when running in - // production mode and "alwaysFix" is specified in the plugin options - taskSession.logger.terminal.writeLine( - 'Fix mode has been disabled since Heft is running in production mode' - ); - fix = false; - } - - const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath; - const sarifLogPath: string | undefined = - relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); - - // To support standalone linting, track if we have hooked to the typescript plugin - let inTypescriptPhase: boolean = false; + const fix: boolean = this._checkFix(taskSession, pluginOptions); + const sarifLogPath: string | undefined = this._getSarifLogPath(heftConfiguration, pluginOptions); // Use the changed files hook to kick off linting asynchronously taskSession.requestAccessToPluginByName( '@rushstack/heft-typescript-plugin', @@ -114,38 +119,6 @@ export default class LintPlugin implements IHeftTaskPlugin { inTypescriptPhase = true; } ); - - if (!inTypescriptPhase) { - // Create a typescript program from the tsconfig file - const tsconfigPath: string = path.resolve(heftConfiguration.buildFolderPath, 'tsconfig.json'); - const parsed: ParsedCommandLine = parseJsonConfigFileContent( - readConfigFile(tsconfigPath, sys.readFile).config, - ts.sys, - path.dirname(tsconfigPath) - ); - const program: IExtendedProgram = createProgram({ - rootNames: parsed.fileNames, - options: parsed.options - }) as IExtendedProgram; - - // Filter out node_modules since we just want to lint the source files in the package only - const changedFiles: ReadonlySet = new Set( - program.getSourceFiles().filter((file) => !file.fileName.includes('node_modules')) - ); - const lintingPromise: Promise = this._lintAsync({ - taskSession, - heftConfiguration, - fix, - sarifLogPath, - tsProgram: program, - changedFiles: changedFiles - }); - lintingPromise.catch(() => { - // Suppress unhandled promise rejection error - }); - // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails - this._lintingPromises.push(lintingPromise); - } } let warningPrinted: boolean = false; @@ -161,11 +134,65 @@ export default class LintPlugin implements IHeftTaskPlugin { // Warn since don't run the linters when in watch mode. taskSession.logger.terminal.writeWarningLine("Linting isn't currently supported in watch mode"); } else { + if (!inTypescriptPhase) { + const fix: boolean = this._checkFix(taskSession, pluginOptions); + const sarifLogPath: string | undefined = this._getSarifLogPath(heftConfiguration, pluginOptions); + // If we are not in the typescript phase, we need to create a typescript program + // from the tsconfig file + const tsProgram: IExtendedProgram = await this._createTypescriptProgramAsync( + heftConfiguration, + taskSession + ); + const rootFiles: readonly string[] = tsProgram.getRootFileNames(); + const changedFiles: Set = new Set(); + rootFiles.forEach((rootFilePath: string) => { + const sourceFile: TTypescript.SourceFile | undefined = tsProgram.getSourceFile(rootFilePath); + changedFiles.add(sourceFile as IExtendedSourceFile); + }); + + const lintingPromise: Promise = this._lintAsync({ + taskSession, + heftConfiguration, + fix, + sarifLogPath, + tsProgram, + changedFiles + }); + lintingPromise.catch(() => { + // Suppress unhandled promise rejection error + }); + // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails + this._lintingPromises.push(lintingPromise); + } await Promise.all(this._lintingPromises); } }); } + private async _createTypescriptProgramAsync( + heftConfiguration: HeftConfiguration, + taskSession: IHeftTaskSession + ): Promise { + const typescriptPath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( + 'typescript', + taskSession.logger.terminal + ); + const ts: typeof TTypescript = await import(typescriptPath); + // Create a typescript program from the tsconfig file + const tsconfigPath: string = path.resolve(heftConfiguration.buildFolderPath, 'tsconfig.json'); + const parsed: TTypescript.ParsedCommandLine = ts.parseJsonConfigFileContent( + ts.readConfigFile(tsconfigPath, ts.sys.readFile).config, + ts.sys, + path.dirname(tsconfigPath) + ); + const program: IExtendedProgram = ts.createProgram({ + rootNames: parsed.fileNames, + options: parsed.options + }) as IExtendedProgram; + + return program; + } + private async _ensureInitializedAsync( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration From 1f31c7e70ef3aee918bb713a84d9fe0317f2a786 Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Mon, 2 Jun 2025 13:09:58 -0700 Subject: [PATCH 3/8] Moves typescript dependency back to devDependencies --- heft-plugins/heft-lint-plugin/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/heft-plugins/heft-lint-plugin/package.json b/heft-plugins/heft-lint-plugin/package.json index 8864ff42891..a4f46bcb6b6 100644 --- a/heft-plugins/heft-lint-plugin/package.json +++ b/heft-plugins/heft-lint-plugin/package.json @@ -19,8 +19,7 @@ }, "dependencies": { "@rushstack/node-core-library": "workspace:*", - "semver": "~7.5.4", - "typescript": "~5.8.2" + "semver": "~7.5.4" }, "devDependencies": { "@rushstack/heft-typescript-plugin": "workspace:*", @@ -30,6 +29,7 @@ "@types/semver": "7.5.0", "decoupled-local-node-rig": "workspace:*", "eslint": "~8.57.0", + "typescript": "~5.8.2", "tslint": "~5.20.1" } } From be6af1c57fedff9ddc02af126cf0f65c5156e751 Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Mon, 2 Jun 2025 13:13:23 -0700 Subject: [PATCH 4/8] Updates repo-state.json --- common/config/subspaces/build-tests-subspace/repo-state.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/config/subspaces/build-tests-subspace/repo-state.json b/common/config/subspaces/build-tests-subspace/repo-state.json index f4d7ca8a804..0e051290a8f 100644 --- a/common/config/subspaces/build-tests-subspace/repo-state.json +++ b/common/config/subspaces/build-tests-subspace/repo-state.json @@ -2,5 +2,5 @@ { "pnpmShrinkwrapHash": "02a170ed391200c1c2013b084044508d86c0cc9a", "preferredVersionsHash": "54149ea3f01558a859c96dee2052b797d4defe68", - "packageJsonInjectedDependenciesHash": "fe10df2247513902b2612f6730996b58d02b03a9" + "packageJsonInjectedDependenciesHash": "8a970d796e34c99b14e527e95021fa210d4fd4ac" } From 5f3a55a8b55e420a81122cb1b9d2628e821503fa Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Mon, 2 Jun 2025 13:46:15 -0700 Subject: [PATCH 5/8] Fixes pnpm-lock.yaml --- common/config/subspaces/default/pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 9497b3ad56d..01fcb5e260e 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -2708,9 +2708,6 @@ importers: semver: specifier: ~7.5.4 version: 7.5.4 - typescript: - specifier: ~5.8.2 - version: 5.8.2 devDependencies: '@rushstack/heft': specifier: workspace:* @@ -2736,6 +2733,9 @@ importers: tslint: specifier: ~5.20.1 version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 ../../../heft-plugins/heft-localization-typings-plugin: dependencies: From 772829d703d4a0052d99a0b0e2a82b6d5bcc6baf Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Mon, 2 Jun 2025 15:03:21 -0700 Subject: [PATCH 6/8] Minor review notes cleanup --- .../heft-lint-plugin/src/LintPlugin.ts | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts index a65cdaa3bc4..34005d308a0 100644 --- a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts +++ b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts @@ -44,6 +44,30 @@ interface ILintOptions { changedFiles?: ReadonlySet; } +function checkFix(taskSession: IHeftTaskSession, pluginOptions?: ILintPluginOptions): boolean { + let fix: boolean = + pluginOptions?.alwaysFix || taskSession.parameters.getFlagParameter(FIX_PARAMETER_NAME).value; + if (fix && taskSession.parameters.production) { + // Write this as a standard output message since we don't want to throw errors when running in + // production mode and "alwaysFix" is specified in the plugin options + taskSession.logger.terminal.writeLine( + 'Fix mode has been disabled since Heft is running in production mode' + ); + fix = false; + } + return fix; +} + +function getSarifLogPath( + heftConfiguration: HeftConfiguration, + pluginOptions?: ILintPluginOptions +): string | undefined { + const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath; + const sarifLogPath: string | undefined = + relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); + return sarifLogPath; +} + export default class LintPlugin implements IHeftTaskPlugin { private readonly _lintingPromises: Promise[] = []; @@ -54,30 +78,6 @@ export default class LintPlugin implements IHeftTaskPlugin { private _tslintToolPath: string | undefined; private _tslintConfigFilePath: string | undefined; - private _checkFix(taskSession: IHeftTaskSession, pluginOptions?: ILintPluginOptions): boolean { - let fix: boolean = - pluginOptions?.alwaysFix || taskSession.parameters.getFlagParameter(FIX_PARAMETER_NAME).value; - if (fix && taskSession.parameters.production) { - // Write this as a standard output message since we don't want to throw errors when running in - // production mode and "alwaysFix" is specified in the plugin options - taskSession.logger.terminal.writeLine( - 'Fix mode has been disabled since Heft is running in production mode' - ); - fix = false; - } - return fix; - } - - private _getSarifLogPath( - heftConfiguration: HeftConfiguration, - pluginOptions?: ILintPluginOptions - ): string | undefined { - const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath; - const sarifLogPath: string | undefined = - relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); - return sarifLogPath; - } - public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, @@ -89,8 +89,8 @@ export default class LintPlugin implements IHeftTaskPlugin { // Disable linting in watch mode. Some lint rules require the context of multiple files, which // may not be available in watch mode. if (!taskSession.parameters.watch) { - const fix: boolean = this._checkFix(taskSession, pluginOptions); - const sarifLogPath: string | undefined = this._getSarifLogPath(heftConfiguration, pluginOptions); + const fix: boolean = checkFix(taskSession, pluginOptions); + const sarifLogPath: string | undefined = getSarifLogPath(heftConfiguration, pluginOptions); // Use the changed files hook to kick off linting asynchronously taskSession.requestAccessToPluginByName( '@rushstack/heft-typescript-plugin', @@ -135,8 +135,8 @@ export default class LintPlugin implements IHeftTaskPlugin { taskSession.logger.terminal.writeWarningLine("Linting isn't currently supported in watch mode"); } else { if (!inTypescriptPhase) { - const fix: boolean = this._checkFix(taskSession, pluginOptions); - const sarifLogPath: string | undefined = this._getSarifLogPath(heftConfiguration, pluginOptions); + const fix: boolean = checkFix(taskSession, pluginOptions); + const sarifLogPath: string | undefined = getSarifLogPath(heftConfiguration, pluginOptions); // If we are not in the typescript phase, we need to create a typescript program // from the tsconfig file const tsProgram: IExtendedProgram = await this._createTypescriptProgramAsync( @@ -162,9 +162,10 @@ export default class LintPlugin implements IHeftTaskPlugin { // Suppress unhandled promise rejection error }); // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails - this._lintingPromises.push(lintingPromise); + await lintingPromise; + } else { + await Promise.all(this._lintingPromises); } - await Promise.all(this._lintingPromises); } }); } From e59fb9a938bf50820df6b1d3ce2c3c0f60f8d030 Mon Sep 17 00:00:00 2001 From: Ben Delarre Date: Mon, 2 Jun 2025 17:33:08 -0700 Subject: [PATCH 7/8] No need to handle catch case in awaited promise --- heft-plugins/heft-lint-plugin/src/LintPlugin.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts index 34005d308a0..030405d0b95 100644 --- a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts +++ b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts @@ -150,7 +150,7 @@ export default class LintPlugin implements IHeftTaskPlugin { changedFiles.add(sourceFile as IExtendedSourceFile); }); - const lintingPromise: Promise = this._lintAsync({ + await this._lintAsync({ taskSession, heftConfiguration, fix, @@ -158,11 +158,6 @@ export default class LintPlugin implements IHeftTaskPlugin { tsProgram, changedFiles }); - lintingPromise.catch(() => { - // Suppress unhandled promise rejection error - }); - // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails - await lintingPromise; } else { await Promise.all(this._lintingPromises); } From f826621dd2b623d041fb8fb3706cc77bc91e369b Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Thu, 5 Jun 2025 19:11:59 -0400 Subject: [PATCH 8/8] Rush change. --- ...enjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json b/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json index 929c6f6d781..c519a23f9c8 100644 --- a/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json +++ b/common/changes/@rushstack/heft-lint-plugin/benjamind-heft-lint-plugin-standalone_2025-05-29-00-49.json @@ -2,8 +2,8 @@ "changes": [ { "packageName": "@rushstack/heft-lint-plugin", - "comment": "Adds support for using heft-lint-plugin standalone without a typescript phase", - "type": "patch" + "comment": "Add support for using heft-lint-plugin standalone without a typescript phase", + "type": "minor" } ], "packageName": "@rushstack/heft-lint-plugin"