@rushstack/heft-lint-plugin
Advanced tools
Comparing version 0.3.47 to 0.3.48
@@ -5,2 +5,14 @@ { | ||
{ | ||
"version": "0.3.48", | ||
"tag": "@rushstack/heft-lint-plugin_v0.3.48", | ||
"date": "Tue, 13 Aug 2024 18:17:05 GMT", | ||
"comments": { | ||
"patch": [ | ||
{ | ||
"comment": "Supported linters (ESLint, TSLint) are now loaded asynchronously" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"version": "0.3.47", | ||
@@ -7,0 +19,0 @@ "tag": "@rushstack/heft-lint-plugin_v0.3.47", |
# Change Log - @rushstack/heft-lint-plugin | ||
This log was last generated on Mon, 12 Aug 2024 22:16:04 GMT and should not be manually modified. | ||
This log was last generated on Tue, 13 Aug 2024 18:17:05 GMT and should not be manually modified. | ||
## 0.3.48 | ||
Tue, 13 Aug 2024 18:17:05 GMT | ||
### Patches | ||
- Supported linters (ESLint, TSLint) are now loaded asynchronously | ||
## 0.3.47 | ||
@@ -6,0 +13,0 @@ Mon, 12 Aug 2024 22:16:04 GMT |
@@ -5,19 +5,18 @@ import type * as TTypescript from 'typescript'; | ||
interface IEslintOptions extends ILinterBaseOptions { | ||
eslintPackagePath: string; | ||
eslintPackage: typeof TEslint; | ||
eslintTimings: Map<string, number>; | ||
} | ||
export declare class Eslint extends LinterBase<TEslint.ESLint.LintResult> { | ||
private readonly _eslintPackagePath; | ||
private readonly _eslintPackage; | ||
private readonly _linter; | ||
private readonly _eslintTimings; | ||
private _eslintPackage; | ||
private _eslint; | ||
constructor(options: IEslintOptions); | ||
protected constructor(options: IEslintOptions); | ||
static initializeAsync(options: ILinterBaseOptions): Promise<Eslint>; | ||
printVersionHeader(): void; | ||
protected getCacheVersionAsync(): Promise<string>; | ||
protected initializeAsync(tsProgram: TTypescript.Program): Promise<void>; | ||
protected lintFileAsync(sourceFile: TTypescript.SourceFile): Promise<TEslint.ESLint.LintResult[]>; | ||
protected lintingFinished(lintFailures: TEslint.ESLint.LintResult[]): void; | ||
protected isFileExcludedAsync(filePath: string): Promise<boolean>; | ||
private _patchTimer; | ||
} | ||
export {}; | ||
//# sourceMappingURL=Eslint.d.ts.map |
@@ -40,2 +40,18 @@ "use strict"; | ||
})(EslintMessageSeverity || (EslintMessageSeverity = {})); | ||
async function patchTimerAsync(eslintPackagePath, timingsMap) { | ||
const timingModulePath = path.join(eslintPackagePath, 'lib', 'linter', 'timing'); | ||
const timing = (await Promise.resolve(`${timingModulePath}`).then(s => __importStar(require(s)))).default; | ||
timing.enabled = true; | ||
const patchedTime = (key, fn) => { | ||
return (...args) => { | ||
const startTime = perf_hooks_1.performance.now(); | ||
const result = fn(...args); | ||
const endTime = perf_hooks_1.performance.now(); | ||
const existingTiming = timingsMap.get(key) || 0; | ||
timingsMap.set(key, existingTiming + endTime - startTime); | ||
return result; | ||
}; | ||
}; | ||
timing.time = patchedTime; | ||
} | ||
class Eslint extends LinterBase_1.LinterBase { | ||
@@ -45,12 +61,32 @@ constructor(options) { | ||
this._eslintTimings = new Map(); | ||
this._eslintPackagePath = options.eslintPackagePath; | ||
const { buildFolderPath, eslintPackage, linterConfigFilePath, tsProgram, eslintTimings } = options; | ||
this._eslintPackage = eslintPackage; | ||
this._linter = new eslintPackage.ESLint({ | ||
cwd: buildFolderPath, | ||
overrideConfigFile: linterConfigFilePath, | ||
// Override config takes precedence over overrideConfigFile, which allows us to provide | ||
// the source TypeScript program to ESLint | ||
overrideConfig: { | ||
parserOptions: { | ||
programs: [tsProgram] | ||
} | ||
} | ||
}); | ||
this._eslintTimings = eslintTimings; | ||
} | ||
static async initializeAsync(options) { | ||
const { linterToolPath } = options; | ||
const eslintTimings = new Map(); | ||
// This must happen before the rest of the linter package is loaded | ||
this._patchTimer(options.eslintPackagePath); | ||
this._eslintPackage = require(options.eslintPackagePath); | ||
await patchTimerAsync(linterToolPath, eslintTimings); | ||
const eslintPackage = await Promise.resolve(`${linterToolPath}`).then(s => __importStar(require(s))); | ||
return new Eslint(Object.assign(Object.assign({}, options), { eslintPackage, | ||
eslintTimings })); | ||
} | ||
printVersionHeader() { | ||
this._terminal.writeLine(`Using ESLint version ${this._eslintPackage.Linter.version}`); | ||
const majorVersion = semver.major(this._eslintPackage.Linter.version); | ||
const linterVersion = this._eslintPackage.Linter.version; | ||
this._terminal.writeLine(`Using ESLint version ${linterVersion}`); | ||
const majorVersion = semver.major(linterVersion); | ||
if (majorVersion < 7) { | ||
throw new Error('Heft requires ESLint 7 or newer. Your ESLint version is too old:\n' + this._eslintPackagePath); | ||
throw new Error('Heft requires ESLint 7 or newer. Your ESLint version is too old'); | ||
} | ||
@@ -60,4 +96,3 @@ if (majorVersion > 8) { | ||
// a newer ESLint release, their build should be allowed to succeed. | ||
this._terminal.writeLine('The ESLint version is newer than the latest version that was tested with Heft; it may not work correctly:'); | ||
this._terminal.writeLine(this._eslintPackagePath); | ||
this._terminal.writeLine('The ESLint version is newer than the latest version that was tested with Heft, so it may not work correctly.'); | ||
} | ||
@@ -67,3 +102,3 @@ } | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const eslintBaseConfiguration = await this._eslint.calculateConfigForFile(this._linterConfigFilePath); | ||
const eslintBaseConfiguration = await this._linter.calculateConfigForFile(this._linterConfigFilePath); | ||
const eslintConfigHash = crypto | ||
@@ -75,17 +110,4 @@ .createHash('sha1') | ||
} | ||
async initializeAsync(tsProgram) { | ||
// Override config takes precedence over overrideConfigFile, which allows us to provide | ||
// the source TypeScript program. | ||
this._eslint = new this._eslintPackage.ESLint({ | ||
cwd: this._buildFolderPath, | ||
overrideConfigFile: this._linterConfigFilePath, | ||
overrideConfig: { | ||
parserOptions: { | ||
programs: [tsProgram] | ||
} | ||
} | ||
}); | ||
} | ||
async lintFileAsync(sourceFile) { | ||
const lintResults = await this._eslint.lintText(sourceFile.text, { | ||
const lintResults = await this._linter.lintText(sourceFile.text, { | ||
filePath: sourceFile.fileName | ||
@@ -151,21 +173,6 @@ }); | ||
async isFileExcludedAsync(filePath) { | ||
return await this._eslint.isPathIgnored(filePath); | ||
return await this._linter.isPathIgnored(filePath); | ||
} | ||
_patchTimer(eslintPackagePath) { | ||
const timing = require(path.join(eslintPackagePath, 'lib', 'linter', 'timing')); | ||
timing.enabled = true; | ||
const patchedTime = (key, fn) => { | ||
return (...args) => { | ||
const startTime = perf_hooks_1.performance.now(); | ||
const result = fn(...args); | ||
const endTime = perf_hooks_1.performance.now(); | ||
const existingTiming = this._eslintTimings.get(key) || 0; | ||
this._eslintTimings.set(key, existingTiming + endTime - startTime); | ||
return result; | ||
}; | ||
}; | ||
timing.time = patchedTime; | ||
} | ||
} | ||
exports.Eslint = Eslint; | ||
//# sourceMappingURL=Eslint.js.map |
@@ -11,3 +11,5 @@ import type { ITerminal } from '@rushstack/terminal'; | ||
buildMetadataFolderPath: string; | ||
linterToolPath: string; | ||
linterConfigFilePath: string; | ||
tsProgram: IExtendedProgram; | ||
} | ||
@@ -36,3 +38,2 @@ export interface IRunLinterOptions { | ||
protected abstract getCacheVersionAsync(): Promise<string>; | ||
protected abstract initializeAsync(tsProgram: IExtendedProgram): Promise<void>; | ||
protected abstract lintFileAsync(sourceFile: IExtendedSourceFile): Promise<TLintResult[]>; | ||
@@ -39,0 +40,0 @@ protected abstract lintingFinished(lintFailures: TLintResult[]): void; |
@@ -45,3 +45,2 @@ "use strict"; | ||
let fileCount = 0; | ||
await this.initializeAsync(options.tsProgram); | ||
const commonDirectory = options.tsProgram.getCommonSourceDirectory(); | ||
@@ -48,0 +47,0 @@ const relativePaths = new Map(); |
@@ -13,4 +13,3 @@ import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; | ||
private _lintAsync; | ||
private _runEslintAsync; | ||
private _runTslintAsync; | ||
private _runLinterAsync; | ||
private _resolveTslintConfigFilePathAsync; | ||
@@ -17,0 +16,0 @@ private _resolveEslintConfigFilePathAsync; |
@@ -72,23 +72,32 @@ "use strict"; | ||
await this._ensureInitializedAsync(taskSession, heftConfiguration); | ||
// Now that we know we have initialized properly, run the linter(s) | ||
const lintingPromises = []; | ||
if (this._eslintToolPath) { | ||
lintingPromises.push(this._runEslintAsync(taskSession, heftConfiguration, this._eslintToolPath, this._eslintConfigFilePath, tsProgram, changedFiles)); | ||
const linters = []; | ||
if (this._eslintConfigFilePath && this._eslintToolPath) { | ||
const eslintLinter = await Eslint_1.Eslint.initializeAsync({ | ||
tsProgram, | ||
scopedLogger: taskSession.logger, | ||
linterToolPath: this._eslintToolPath, | ||
linterConfigFilePath: this._eslintConfigFilePath, | ||
buildFolderPath: heftConfiguration.buildFolderPath, | ||
buildMetadataFolderPath: taskSession.tempFolderPath | ||
}); | ||
linters.push(eslintLinter); | ||
} | ||
if (this._tslintToolPath) { | ||
lintingPromises.push(this._runTslintAsync(taskSession, heftConfiguration, this._tslintToolPath, this._tslintConfigFilePath, tsProgram, changedFiles)); | ||
if (this._tslintConfigFilePath && this._tslintToolPath) { | ||
const tslintLinter = await Tslint_1.Tslint.initializeAsync({ | ||
tsProgram, | ||
scopedLogger: taskSession.logger, | ||
linterToolPath: this._tslintToolPath, | ||
linterConfigFilePath: this._tslintConfigFilePath, | ||
buildFolderPath: heftConfiguration.buildFolderPath, | ||
buildMetadataFolderPath: taskSession.tempFolderPath | ||
}); | ||
linters.push(tslintLinter); | ||
} | ||
await Promise.all(lintingPromises); | ||
// Now that we know we have initialized properly, run the linter(s) | ||
await Promise.all(linters.map((linter) => this._runLinterAsync(linter, tsProgram, changedFiles))); | ||
} | ||
async _runEslintAsync(taskSession, heftConfiguration, eslintToolPath, eslintConfigFilePath, tsProgram, changedFiles) { | ||
const eslint = new Eslint_1.Eslint({ | ||
scopedLogger: taskSession.logger, | ||
eslintPackagePath: eslintToolPath, | ||
linterConfigFilePath: eslintConfigFilePath, | ||
buildFolderPath: heftConfiguration.buildFolderPath, | ||
buildMetadataFolderPath: taskSession.tempFolderPath | ||
}); | ||
eslint.printVersionHeader(); | ||
async _runLinterAsync(linter, tsProgram, changedFiles) { | ||
linter.printVersionHeader(); | ||
const typeScriptFilenames = new Set(tsProgram.getRootFileNames()); | ||
await eslint.performLintingAsync({ | ||
await linter.performLintingAsync({ | ||
tsProgram, | ||
@@ -99,18 +108,2 @@ typeScriptFilenames, | ||
} | ||
async _runTslintAsync(taskSession, heftConfiguration, tslintToolPath, tslintConfigFilePath, tsProgram, changedFiles) { | ||
const tslint = new Tslint_1.Tslint({ | ||
scopedLogger: taskSession.logger, | ||
tslintPackagePath: tslintToolPath, | ||
linterConfigFilePath: tslintConfigFilePath, | ||
buildFolderPath: heftConfiguration.buildFolderPath, | ||
buildMetadataFolderPath: taskSession.tempFolderPath | ||
}); | ||
tslint.printVersionHeader(); | ||
const typeScriptFilenames = new Set(tsProgram.getRootFileNames()); | ||
await tslint.performLintingAsync({ | ||
tsProgram, | ||
typeScriptFilenames, | ||
changedFiles: changedFiles || new Set(tsProgram.getSourceFiles()) | ||
}); | ||
} | ||
async _resolveTslintConfigFilePathAsync(heftConfiguration) { | ||
@@ -117,0 +110,0 @@ const tslintConfigFilePath = `${heftConfiguration.buildFolderPath}/tslint.json`; |
@@ -8,11 +8,13 @@ /// <reference types="node" /> | ||
interface ITslintOptions extends ILinterBaseOptions { | ||
tslintPackagePath: string; | ||
tslintPackage: typeof TTslint; | ||
tslintConfiguration: TTslint.Configuration.IConfigurationFile; | ||
} | ||
export declare class Tslint extends LinterBase<TTslint.RuleFailure> { | ||
private _tslint; | ||
private _tslintConfiguration; | ||
private _linter; | ||
private _enabledRules; | ||
private _ruleSeverityMap; | ||
private readonly _tslintPackage; | ||
private readonly _tslintConfiguration; | ||
private readonly _linter; | ||
private readonly _enabledRules; | ||
private readonly _ruleSeverityMap; | ||
constructor(options: ITslintOptions); | ||
static initializeAsync(options: ILinterBaseOptions): Promise<Tslint>; | ||
/** | ||
@@ -29,8 +31,8 @@ * Returns the sha1 hash of the contents of the config file at the provided path and the | ||
protected getCacheVersionAsync(): Promise<string>; | ||
protected initializeAsync(tsProgram: TTypescript.Program): Promise<void>; | ||
protected lintFileAsync(sourceFile: TTypescript.SourceFile): Promise<TTslint.RuleFailure[]>; | ||
protected lintingFinished(failures: TTslint.RuleFailure[]): void; | ||
protected isFileExcludedAsync(filePath: string): Promise<boolean>; | ||
private _getLintFileError; | ||
} | ||
export {}; | ||
//# sourceMappingURL=Tslint.d.ts.map |
@@ -33,7 +33,30 @@ "use strict"; | ||
const LinterBase_1 = require("./LinterBase"); | ||
function getFormattedErrorMessage(tslintFailure) { | ||
return `(${tslintFailure.getRuleName()}) ${tslintFailure.getFailure()}`; | ||
} | ||
class Tslint extends LinterBase_1.LinterBase { | ||
constructor(options) { | ||
super('tslint', options); | ||
this._tslint = require(options.tslintPackagePath); | ||
const { tslintPackage, tsProgram } = options; | ||
this._tslintPackage = tslintPackage; | ||
this._tslintConfiguration = options.tslintConfiguration; | ||
this._linter = new tslintPackage.Linter({ | ||
// This is not handled by the linter in the way that we use it, so we will manually apply | ||
// fixes later | ||
fix: false, | ||
rulesDirectory: this._tslintConfiguration.rulesDirectory | ||
}, tsProgram); | ||
this._enabledRules = this._linter.getEnabledRules(this._tslintConfiguration, false); | ||
this._ruleSeverityMap = new Map(this._enabledRules.map((rule) => [ | ||
rule.getOptions().ruleName, | ||
rule.getOptions().ruleSeverity | ||
])); | ||
} | ||
static async initializeAsync(options) { | ||
const { linterToolPath, linterConfigFilePath } = options; | ||
const tslintPackage = await Promise.resolve(`${linterToolPath}`).then(s => __importStar(require(s))); | ||
const tslintConfiguration = tslintPackage.Configuration.loadConfigurationFromPath(linterConfigFilePath); | ||
return new Tslint(Object.assign(Object.assign({}, options), { tslintPackage, | ||
tslintConfiguration })); | ||
} | ||
/** | ||
@@ -82,21 +105,9 @@ * Returns the sha1 hash of the contents of the config file at the provided path and the | ||
printVersionHeader() { | ||
this._terminal.writeLine(`Using TSLint version ${this._tslint.Linter.VERSION}`); | ||
this._terminal.writeLine(`Using TSLint version ${this._tslintPackage.Linter.VERSION}`); | ||
} | ||
async getCacheVersionAsync() { | ||
const tslintConfigHash = await Tslint.getConfigHashAsync(this._linterConfigFilePath, this._terminal); | ||
const tslintConfigVersion = `${this._tslint.Linter.VERSION}_${tslintConfigHash.digest('hex')}`; | ||
const tslintConfigVersion = `${this._tslintPackage.Linter.VERSION}_${tslintConfigHash.digest('hex')}`; | ||
return tslintConfigVersion; | ||
} | ||
async initializeAsync(tsProgram) { | ||
this._tslintConfiguration = this._tslint.Configuration.loadConfigurationFromPath(this._linterConfigFilePath); | ||
this._linter = new this._tslint.Linter({ | ||
fix: false, | ||
rulesDirectory: this._tslintConfiguration.rulesDirectory | ||
}, tsProgram); | ||
this._enabledRules = this._linter.getEnabledRules(this._tslintConfiguration, false); | ||
this._ruleSeverityMap = new Map(this._enabledRules.map((rule) => [ | ||
rule.getOptions().ruleName, | ||
rule.getOptions().ruleSeverity | ||
])); | ||
} | ||
async lintFileAsync(sourceFile) { | ||
@@ -122,10 +133,3 @@ // Some of this code comes from here: | ||
for (const tslintFailure of lintResult.failures) { | ||
const { line, character } = tslintFailure.getStartPosition().getLineAndCharacter(); | ||
const formattedFailure = `(${tslintFailure.getRuleName()}) ${tslintFailure.getFailure()}`; | ||
const errorObject = new node_core_library_1.FileError(formattedFailure, { | ||
absolutePath: tslintFailure.getFileName(), | ||
projectFolder: this._buildFolderPath, | ||
line: line + 1, | ||
column: character + 1 | ||
}); | ||
const errorObject = this._getLintFileError(tslintFailure); | ||
switch (tslintFailure.getRuleSeverity()) { | ||
@@ -145,6 +149,18 @@ case 'error': { | ||
async isFileExcludedAsync(filePath) { | ||
return this._tslint.Configuration.isFileExcluded(filePath, this._tslintConfiguration); | ||
return this._tslintPackage.Configuration.isFileExcluded(filePath, this._tslintConfiguration); | ||
} | ||
_getLintFileError(tslintFailure, message) { | ||
if (!message) { | ||
message = getFormattedErrorMessage(tslintFailure); | ||
} | ||
const { line, character } = tslintFailure.getStartPosition().getLineAndCharacter(); | ||
return new node_core_library_1.FileError(message, { | ||
absolutePath: tslintFailure.getFileName(), | ||
projectFolder: this._buildFolderPath, | ||
line: line + 1, | ||
column: character + 1 | ||
}); | ||
} | ||
} | ||
exports.Tslint = Tslint; | ||
//# sourceMappingURL=Tslint.js.map |
{ | ||
"name": "@rushstack/heft-lint-plugin", | ||
"version": "0.3.47", | ||
"version": "0.3.48", | ||
"description": "A Heft plugin for using ESLint or TSLint. Intended for use with @rushstack/heft-typescript-plugin", | ||
@@ -29,5 +29,5 @@ "repository": { | ||
"local-eslint-config": "1.0.0", | ||
"@rushstack/heft": "0.66.26", | ||
"@rushstack/heft-typescript-plugin": "0.5.24", | ||
"@rushstack/terminal": "0.13.4" | ||
"@rushstack/terminal": "0.13.4", | ||
"@rushstack/heft": "0.66.26" | ||
}, | ||
@@ -34,0 +34,0 @@ "scripts": { |
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
154020
2548