diff --git a/package-lock.json b/package-lock.json index 6bea7717..8b1da131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8773,10 +8773,10 @@ }, "packages/code-analyzer-core": { "name": "@salesforce/code-analyzer-core", - "version": "0.33.0", + "version": "0.34.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "csv-stringify": "^6.6.0", "js-yaml": "^4.1.0", @@ -9033,7 +9033,7 @@ }, "packages/code-analyzer-engine-api": { "name": "@salesforce/code-analyzer-engine-api", - "version": "0.27.0", + "version": "0.28.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { "@types/node": "^20.0.0", @@ -9286,15 +9286,15 @@ }, "packages/code-analyzer-eslint-engine": { "name": "@salesforce/code-analyzer-eslint-engine", - "version": "0.30.0", + "version": "0.31.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { "@eslint/js": "^9.32.0", "@lwc/eslint-plugin-lwc": "^3.2.0", "@lwc/eslint-plugin-lwc-platform": "^6.1.0", "@salesforce-ux/eslint-plugin-slds": "^0.5.0", - "@salesforce/code-analyzer-engine-api": "0.27.0", - "@salesforce/code-analyzer-eslint8-engine": "0.5.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", + "@salesforce/code-analyzer-eslint8-engine": "0.6.0-SNAPSHOT", "@salesforce/eslint-config-lwc": "^4.0.0", "@salesforce/eslint-plugin-lightning": "^2.0.0", "@types/node": "^20.0.0", @@ -9833,7 +9833,7 @@ }, "packages/code-analyzer-eslint8-engine": { "name": "@salesforce/code-analyzer-eslint8-engine", - "version": "0.5.0", + "version": "0.6.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { "@babel/core": "7.27.4", @@ -9841,7 +9841,7 @@ "@eslint/js": "8.57.1", "@lwc/eslint-plugin-lwc": "2.2.0", "@lwc/eslint-plugin-lwc-platform": "5.2.0", - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@salesforce/eslint-config-lwc": "3.7.2", "@salesforce/eslint-plugin-lightning": "1.0.1", "@types/node": "^20.0.0", @@ -10233,10 +10233,10 @@ }, "packages/code-analyzer-flow-engine": { "name": "@salesforce/code-analyzer-flow-engine", - "version": "0.25.0", + "version": "0.26.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -10487,10 +10487,10 @@ }, "packages/code-analyzer-pmd-engine": { "name": "@salesforce/code-analyzer-pmd-engine", - "version": "0.29.0", + "version": "0.30.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -10741,10 +10741,10 @@ }, "packages/code-analyzer-regex-engine": { "name": "@salesforce/code-analyzer-regex-engine", - "version": "0.26.0", + "version": "0.27.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "isbinaryfile": "^5.0.4", "p-limit": "^3.1.0" @@ -10995,10 +10995,10 @@ }, "packages/code-analyzer-retirejs-engine": { "name": "@salesforce/code-analyzer-retirejs-engine", - "version": "0.25.0", + "version": "0.26.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "isbinaryfile": "^5.0.4", "node-stream-zip": "^1.15.0", @@ -11250,10 +11250,10 @@ }, "packages/code-analyzer-sfge-engine": { "name": "@salesforce/code-analyzer-sfge-engine", - "version": "0.11.0", + "version": "0.12.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -11507,7 +11507,7 @@ "version": "0.1.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0" }, "devDependencies": { @@ -11759,7 +11759,7 @@ "version": "0.1.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0" }, "devDependencies": { @@ -12011,7 +12011,7 @@ "version": "0.1.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0" }, "devDependencies": { diff --git a/packages/ENGINE-TEMPLATE/package.json b/packages/ENGINE-TEMPLATE/package.json index 43206805..12226c1a 100644 --- a/packages/ENGINE-TEMPLATE/package.json +++ b/packages/ENGINE-TEMPLATE/package.json @@ -14,7 +14,7 @@ "types": "dist/index.d.ts", "dependencies": { "@types/node": "^20.0.0", - "@salesforce/code-analyzer-engine-api": "0.27.0" + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT" }, "devDependencies": { "@eslint/js": "^9.32.0", @@ -60,4 +60,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/T-E-M-P-L-A-T-E/package.json b/packages/T-E-M-P-L-A-T-E/package.json index e41b1894..8a3dd4ea 100644 --- a/packages/T-E-M-P-L-A-T-E/package.json +++ b/packages/T-E-M-P-L-A-T-E/package.json @@ -14,7 +14,7 @@ "types": "dist/index.d.ts", "dependencies": { "@types/node": "^20.0.0", - "@salesforce/code-analyzer-engine-api": "0.27.0" + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT" }, "devDependencies": { "@eslint/js": "^9.32.0", @@ -60,4 +60,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-core/package.json b/packages/code-analyzer-core/package.json index f4493704..eb0cf4d8 100644 --- a/packages/code-analyzer-core/package.json +++ b/packages/code-analyzer-core/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-core", "description": "Core Package for the Salesforce Code Analyzer", - "version": "0.33.0", + "version": "0.34.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "csv-stringify": "^6.6.0", "js-yaml": "^4.1.0", @@ -69,4 +69,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-core/src/code-analyzer.ts b/packages/code-analyzer-core/src/code-analyzer.ts index 6e435704..007c95bf 100644 --- a/packages/code-analyzer-core/src/code-analyzer.ts +++ b/packages/code-analyzer-core/src/code-analyzer.ts @@ -24,7 +24,13 @@ import * as engApi from "@salesforce/code-analyzer-engine-api" import {Clock, RealClock} from '@salesforce/code-analyzer-engine-api/utils'; import {EventEmitter} from "node:events"; import {CodeAnalyzerConfig, ConfigDescription, EngineOverrides, FIELDS, RuleOverride} from "./config"; -import {EngineProgressAggregator, RuntimeUniqueIdGenerator, toAbsolutePath, UniqueIdGenerator} from "./utils"; +import { + EngineProgressAggregator, RuntimeTempFolder, + RuntimeUniqueIdGenerator, + TempFolder, + toAbsolutePath, + UniqueIdGenerator +} from "./utils"; import fs from "node:fs"; import path from 'node:path'; @@ -94,6 +100,7 @@ const MINIMUM_SUPPORTED_NODE = 20; export class CodeAnalyzer { private readonly config: CodeAnalyzerConfig; private clock: Clock = new RealClock(); + private tempFolder: TempFolder = new RuntimeTempFolder(); private uniqueIdGenerator: UniqueIdGenerator = new RuntimeUniqueIdGenerator(); private readonly eventEmitter: EventEmitter = new EventEmitter(); private readonly engines: Map = new Map(); @@ -118,12 +125,15 @@ export class CodeAnalyzer { } // For testing purposes only - _setClock(clock: Clock) { + _setClock(clock: Clock): void { this.clock = clock; } - _setUniqueIdGenerator(uniqueIdGenerator: UniqueIdGenerator) { + _setUniqueIdGenerator(uniqueIdGenerator: UniqueIdGenerator): void { this.uniqueIdGenerator = uniqueIdGenerator; } + _setTempFolder(tempFolder: TempFolder): void { + this.tempFolder = tempFolder; + } /** * Convenience method to return the same CodeAnalyzerConfig instance that was provided to the constructor @@ -296,15 +306,19 @@ export class CodeAnalyzer { // called a second time before the first call to run hasn't finished. This can occur if someone builds // up a bunch of RunResults promises and then does a Promise.all on them. Otherwise, the progress events may // override each other. + const runWorkingFolderName: string = `code-analyzer-run-${this.clock.formatToDateTimeString()}`; - const engineRunOptions: engApi.RunOptions = extractEngineRunOptions(runOptions, this.config.getLogFolder()); this.emitLogEvent(LogLevel.Debug, getMessage('RunningWithWorkspace', JSON.stringify({ filesAndFolders: runOptions.workspace.getRawFilesAndFolders(), targets: runOptions.workspace.getRawTargets() }))); const runPromises: Promise[] = ruleSelection.getEngineNames().map( - engineName => this.runEngineAndValidateResults(engineName, ruleSelection, engineRunOptions)); + async (engineName) => this.runEngineAndValidateResults(engineName, ruleSelection, { + logFolder: this.config.getLogFolder(), + workingFolder: await this.tempFolder.createSubfolder(runWorkingFolderName, engineName), + workspace: toEngApiWorkspace(runOptions.workspace) + })); const engineRunResultsList: EngineRunResults[] = await Promise.all(runPromises); const runResults: RunResultsImpl = new RunResultsImpl(this.clock); @@ -330,11 +344,16 @@ export class CodeAnalyzer { private async getAllRules(workspace?: Workspace): Promise { const cacheKey: string = workspace ? workspace.getWorkspaceId() : process.cwd(); + const describeWorkingFolderName: string = `code-analyzer-describe-${this.clock.formatToDateTimeString()}`; if (!this.rulesCache.has(cacheKey)) { this.engineRuleDiscoveryProgressAggregator.reset(this.getEngineNames()); const engApiWorkspace: engApi.Workspace | undefined = workspace ? toEngApiWorkspace(workspace) : undefined; - const rulePromises: Promise[] = this.getEngineNames().map(engineName => - this.getAllRulesFor(engineName, {workspace: engApiWorkspace, logFolder: this.config.getLogFolder()})); + const rulePromises: Promise[] = this.getEngineNames().map(async (engineName) => + this.getAllRulesFor(engineName, { + workspace: engApiWorkspace, + workingFolder: await this.tempFolder.createSubfolder(describeWorkingFolderName, engineName), + logFolder: this.config.getLogFolder() + })); this.rulesCache.set(cacheKey, (await Promise.all(rulePromises)).flat()); } return this.rulesCache.get(cacheKey)!; @@ -602,13 +621,6 @@ function validateRuleDescriptions(ruleDescriptions: engApi.RuleDescription[], en } } -function extractEngineRunOptions(runOptions: RunOptions, logFolder: string): engApi.RunOptions { - return { - logFolder: logFolder, - workspace: toEngApiWorkspace(runOptions.workspace), - }; -} - async function validateFileOrFolder(fileOrFolder: string): Promise { const absFileOrFolder: string = toAbsolutePath(fileOrFolder); try { @@ -692,6 +704,7 @@ function validateViolationCodeLocations(violation: engApi.Violation, engineName: engineName, violation.ruleName, codeLocation.endLine, codeLocation.startLine)); } + // istanbul ignore else if (codeLocation.endColumn !== undefined) { if (!isValidLineOrColumn(codeLocation.endColumn)) { throw new Error(getMessage('EngineReturnedViolationWithCodeLocationWithInvalidLineOrColumn', diff --git a/packages/code-analyzer-core/src/utils.ts b/packages/code-analyzer-core/src/utils.ts index 15f0dcbe..bc6cd2cd 100644 --- a/packages/code-analyzer-core/src/utils.ts +++ b/packages/code-analyzer-core/src/utils.ts @@ -1,5 +1,7 @@ -import * as path from "node:path"; -import * as crypto from "node:crypto"; +import path from "node:path"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import {createTempDir} from "@salesforce/code-analyzer-engine-api/utils"; // THIS FILE CONTAINS UTILITIES WHICH ARE USED INTERNALLY ONLY. // None of the following exported interfaces and functions should be exported from the index file. @@ -27,6 +29,28 @@ export class RuntimeUniqueIdGenerator implements UniqueIdGenerator { } } +export interface TempFolder { + getPath(): Promise; + createSubfolder(...subfolderPathSegs: string[]): Promise; +} + +export class RuntimeTempFolder implements TempFolder { + private rootFolder?: string; + + async getPath(): Promise { + if (!this.rootFolder) { + this.rootFolder = await createTempDir(); + } + return this.rootFolder; + } + + async createSubfolder(...subFolderPathSegs: string[]): Promise { + const absPathToSubFolder: string = path.join(await this.getPath(), ...subFolderPathSegs); + await fs.promises.mkdir(absPathToSubFolder, {recursive: true}); + return absPathToSubFolder; + } +} + export class EngineProgressAggregator { private readonly percentagesMap: Map = new Map(); diff --git a/packages/code-analyzer-core/test/code-analyzer.test.ts b/packages/code-analyzer-core/test/code-analyzer.test.ts index e22df4e3..d7d030c7 100644 --- a/packages/code-analyzer-core/test/code-analyzer.test.ts +++ b/packages/code-analyzer-core/test/code-analyzer.test.ts @@ -20,7 +20,7 @@ import { import * as stubs from "./stubs"; import {getMessage} from "../src/messages"; import path from "node:path"; -import {changeWorkingDirectoryToPackageRoot, FixedUniqueIdGenerator} from "./test-helpers"; +import {changeWorkingDirectoryToPackageRoot, FixedUniqueIdGenerator, SimulatedTempFolder} from "./test-helpers"; import * as engApi from "@salesforce/code-analyzer-engine-api" import {FixedClock} from "@salesforce/code-analyzer-engine-api/utils" import {UnexpectedEngineErrorRule} from "../src/rules"; @@ -182,6 +182,7 @@ describe("Tests for the createWorkspace method", () => { describe("Tests for the run method of CodeAnalyzer", () => { let sampleRunOptions: RunOptions; let sampleTimestamp: Date; + let simulatedTempFolder: SimulatedTempFolder; let codeAnalyzer: CodeAnalyzer; let stubEngine1: stubs.StubEngine1; let stubEngine2: stubs.StubEngine2; @@ -193,6 +194,8 @@ describe("Tests for the run method of CodeAnalyzer", () => { sampleTimestamp = new Date(); codeAnalyzer = new CodeAnalyzer(CodeAnalyzerConfig.withDefaults()); codeAnalyzer._setClock(new FixedClock(sampleTimestamp)); + simulatedTempFolder = new SimulatedTempFolder(); + codeAnalyzer._setTempFolder(simulatedTempFolder); codeAnalyzer._setUniqueIdGenerator(new FixedUniqueIdGenerator()); sampleRunOptions = {workspace: await codeAnalyzer.createWorkspace([__dirname])}; const stubPlugin: stubs.StubEnginePlugin = new stubs.StubEnginePlugin(); @@ -209,17 +212,35 @@ describe("Tests for the run method of CodeAnalyzer", () => { ]), }); - const expectedEngineRunOptions: engApi.RunOptions = { + const tempSubfolders: Set = simulatedTempFolder.getCreatedSubfolders(); + + const stubEngine1WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-run') && f.includes('stubEngine1'); + }); + expect(stubEngine1WorkingFolder).toBeDefined(); + const expectedStub1EngineRunOptions: engApi.RunOptions = { logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine1WorkingFolder!, workspace: new engApi.Workspace("FixedId", [SAMPLE_WORKSPACE_FOLDER], [ path.join(SAMPLE_WORKSPACE_FOLDER, 'someFile.cls')]) }; expect(stubEngine1.runRulesCallHistory).toHaveLength(1); expect(stubEngine1.runRulesCallHistory[0].ruleNames).toEqual(expectedStubEngine1RuleNames); - expectEquivalentRunOptions(stubEngine1.runRulesCallHistory[0].runOptions, expectedEngineRunOptions); + expectEquivalentRunOptions(stubEngine1.runRulesCallHistory[0].runOptions, expectedStub1EngineRunOptions); + + const stubEngine2WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-run') && f.includes('stubEngine2'); + }); + expect(stubEngine2WorkingFolder).toBeDefined(); + const expectedStub2EngineRunOptions: engApi.RunOptions = { + logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine2WorkingFolder!, + workspace: new engApi.Workspace("FixedId", [SAMPLE_WORKSPACE_FOLDER], [ + path.join(SAMPLE_WORKSPACE_FOLDER, 'someFile.cls')]) + }; expect(stubEngine2.runRulesCallHistory).toHaveLength(1); expect(stubEngine2.runRulesCallHistory[0].ruleNames).toEqual(expectedStubEngine2RuleNames); - expectEquivalentRunOptions(stubEngine2.runRulesCallHistory[0].runOptions, expectedEngineRunOptions); + expectEquivalentRunOptions(stubEngine2.runRulesCallHistory[0].runOptions, expectedStub2EngineRunOptions); }); it("When the workspace provided is one that is not constructed from CodeAnalyzer's createWorkspace method, then it should still work", async () => { @@ -228,10 +249,17 @@ describe("Tests for the run method of CodeAnalyzer", () => { workspace: dummyWorkspace }); + + const tempSubfolders: Set = simulatedTempFolder.getCreatedSubfolders(); + const stubEngine1WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-run') && f.includes('stubEngine1'); + }); + expect(stubEngine1WorkingFolder).toBeDefined(); expect(stubEngine1.runRulesCallHistory).toEqual([{ ruleNames: expectedStubEngine1RuleNames, runOptions: { logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine1WorkingFolder, workspace: new engApi.Workspace(dummyWorkspace.getWorkspaceId(), dummyWorkspace.getRawFilesAndFolders(), dummyWorkspace.getRawTargets()) } @@ -242,8 +270,14 @@ describe("Tests for the run method of CodeAnalyzer", () => { selection = await codeAnalyzer.selectRules(['stubEngine1:Recommended']); await codeAnalyzer.run(selection, sampleRunOptions); + const tempSubfolders: Set = simulatedTempFolder.getCreatedSubfolders(); + const stubEngine1WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-run') && f.includes('stubEngine1'); + }); + expect(stubEngine1WorkingFolder).toBeDefined(); const expectedEngineRunOptions: engApi.RunOptions = { logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine1WorkingFolder!, workspace: new engApi.Workspace("FixedId", [__dirname]) }; expect(stubEngine1.runRulesCallHistory).toHaveLength(1); @@ -824,6 +858,7 @@ function assertCodeLocation(codeLocation: CodeLocation, file: string, startLine: function expectEquivalentRunOptions(actual: engApi.RunOptions, expected: engApi.RunOptions): void { expect(actual.logFolder).toEqual(expected.logFolder); + expect(actual.workingFolder).toEqual(expected.workingFolder); expectEquivalentWorkspaces(actual.workspace, expected.workspace); } diff --git a/packages/code-analyzer-core/test/rule-selection.test.ts b/packages/code-analyzer-core/test/rule-selection.test.ts index bb3c21b1..c661a7cc 100644 --- a/packages/code-analyzer-core/test/rule-selection.test.ts +++ b/packages/code-analyzer-core/test/rule-selection.test.ts @@ -16,7 +16,7 @@ import * as engApi from "@salesforce/code-analyzer-engine-api" import {FixedClock} from "@salesforce/code-analyzer-engine-api/utils"; import {RepeatedRuleNameEnginePlugin, StubEnginePlugin} from "./stubs"; import path from "node:path"; -import {changeWorkingDirectoryToPackageRoot, FixedUniqueIdGenerator} from "./test-helpers"; +import {changeWorkingDirectoryToPackageRoot, FixedUniqueIdGenerator, SimulatedTempFolder} from "./test-helpers"; import {getMessage} from "../src/messages"; import * as stubs from "./stubs"; @@ -26,6 +26,8 @@ describe('Tests for selecting rules', () => { let codeAnalyzer: CodeAnalyzer; let plugin: StubEnginePlugin; let sampleTimestamp: Date; + let fixedClock: FixedClock; + let simulatedTempFolder: SimulatedTempFolder; async function setupCodeAnalyzer(codeAnalyzer: CodeAnalyzer) : Promise { plugin = new StubEnginePlugin(); @@ -37,7 +39,10 @@ describe('Tests for selecting rules', () => { codeAnalyzer = new CodeAnalyzer(CodeAnalyzerConfig.withDefaults()); await setupCodeAnalyzer(codeAnalyzer); sampleTimestamp = new Date(); - codeAnalyzer._setClock(new FixedClock(sampleTimestamp)); + fixedClock = new FixedClock(sampleTimestamp); + codeAnalyzer._setClock(fixedClock); + simulatedTempFolder = new SimulatedTempFolder(); + codeAnalyzer._setTempFolder(simulatedTempFolder); }) it('When no rule selectors are provided then the Recommended tag is used', async () => { @@ -269,14 +274,31 @@ describe('Tests for selecting rules', () => { it('When selectRules is not provided with SelectOptions, then workspace should be undefined for all engines', async () => { await codeAnalyzer.selectRules(['all']); - const expectedDescribeOptions: engApi.DescribeOptions = { + const tempSubfolders: Set = simulatedTempFolder.getCreatedSubfolders(); + + const stubEngine1WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-describe') && f.includes('stubEngine1'); + }); + expect(stubEngine1WorkingFolder).toBeDefined(); + const expectedStub1DescribeOptions: engApi.DescribeOptions = { logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine1WorkingFolder!, workspace: undefined }; const stubEngine1: stubs.StubEngine1 = plugin.getCreatedEngine('stubEngine1') as stubs.StubEngine1; - expect(stubEngine1.describeRulesCallHistory).toEqual([{describeOptions: expectedDescribeOptions}]); + expect(stubEngine1.describeRulesCallHistory).toEqual([{describeOptions: expectedStub1DescribeOptions}]); + + const stubEngine2WorkingFolder: string|undefined = [...tempSubfolders.keys()].find(f => { + return f.includes('code-analyzer-describe') && f.includes('stubEngine2'); + }); + expect(stubEngine2WorkingFolder).toBeDefined(); + const expectedStub2DescribeOptions: engApi.DescribeOptions = { + logFolder: codeAnalyzer.getConfig().getLogFolder(), + workingFolder: stubEngine2WorkingFolder!, + workspace: undefined + }; const stubEngine2: stubs.StubEngine2 = plugin.getCreatedEngine('stubEngine2') as stubs.StubEngine2; - expect(stubEngine2.describeRulesCallHistory).toEqual([{describeOptions: expectedDescribeOptions}]); + expect(stubEngine2.describeRulesCallHistory).toEqual([{describeOptions: expectedStub2DescribeOptions}]); }); it('When selectRules is provided with SelectOptions, then they are forwarded to the engines', async () => { @@ -285,7 +307,7 @@ describe('Tests for selecting rules', () => { } await codeAnalyzer.selectRules(['all'], selectOptions); - const expectedDescribeOptions: engApi.DescribeOptions = { + const expectedDescribeOptions: Partial = { logFolder: codeAnalyzer.getConfig().getLogFolder(), workspace: new engApi.Workspace('FixedId', [path.resolve('src'), path.resolve('test')]) }; diff --git a/packages/code-analyzer-core/test/test-helpers.ts b/packages/code-analyzer-core/test/test-helpers.ts index 87b89f3b..57f54e05 100644 --- a/packages/code-analyzer-core/test/test-helpers.ts +++ b/packages/code-analyzer-core/test/test-helpers.ts @@ -1,6 +1,6 @@ import process from "node:process"; import path from "node:path"; -import {UniqueIdGenerator} from "../src/utils"; +import {UniqueIdGenerator, TempFolder} from "../src/utils"; export function changeWorkingDirectoryToPackageRoot() { let original_working_directory: string; @@ -27,3 +27,25 @@ export class FixedUniqueIdGenerator implements UniqueIdGenerator { return "FixedUUID"; } } + +export class SimulatedTempFolder implements TempFolder { + private readonly simulatedRoot: string = 'simulatedRoot'; + private subfolderSet: Set = new Set(); + + getPath(): Promise { + return Promise.resolve(this.simulatedRoot); + } + + createSubfolder(...subFolderPathSegs: string[]): Promise { + const joinedPath: string = path.join(this.simulatedRoot, ...subFolderPathSegs); + if (this.subfolderSet.has(joinedPath)) { + throw new Error(`Attempted to create path ${joinedPath} twice`); + } + this.subfolderSet.add(joinedPath); + return Promise.resolve(joinedPath); + } + + getCreatedSubfolders(): Set { + return this.subfolderSet; + } +} diff --git a/packages/code-analyzer-engine-api/package.json b/packages/code-analyzer-engine-api/package.json index 6c0eba0c..09a03af6 100644 --- a/packages/code-analyzer-engine-api/package.json +++ b/packages/code-analyzer-engine-api/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-engine-api", "description": "Engine API Package for the Salesforce Code Analyzer", - "version": "0.27.0", + "version": "0.28.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -65,4 +65,4 @@ "!src/utils/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-engine-api/src/engines.ts b/packages/code-analyzer-engine-api/src/engines.ts index 50727a27..19cf03cb 100644 --- a/packages/code-analyzer-engine-api/src/engines.ts +++ b/packages/code-analyzer-engine-api/src/engines.ts @@ -18,6 +18,12 @@ export type DescribeOptions = { */ logFolder: string + /** + * A temporary folder created specifically for use by the current engine. If the engine ever needs to create temporary + * files or folders, that should be done here. + */ + workingFolder: string + /** * The workspace may or may not be available. If available, then engines should use this workspace object to give a * more accurate list of which of the engine's rules are relevant to the targeted files in the workspace. That is @@ -41,6 +47,12 @@ export type RunOptions = { */ logFolder: string + /** + * A temporary folder created specifically for use by the current engine. If the engine ever needs to create temporary + * files or folders, that should be done here. + */ + workingFolder: string + /** * The workspace object specifying the files that make up the user's workspace and which files should be targeted. * diff --git a/packages/code-analyzer-eslint-engine/package.json b/packages/code-analyzer-eslint-engine/package.json index 5ee1c52c..e96bc77c 100644 --- a/packages/code-analyzer-eslint-engine/package.json +++ b/packages/code-analyzer-eslint-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-eslint-engine", "description": "Plugin package that adds 'eslint' as an engine into Salesforce Code Analyzer", - "version": "0.30.0", + "version": "0.31.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -17,8 +17,8 @@ "@lwc/eslint-plugin-lwc": "^3.2.0", "@lwc/eslint-plugin-lwc-platform": "^6.1.0", "@salesforce-ux/eslint-plugin-slds": "^0.5.0", - "@salesforce/code-analyzer-engine-api": "0.27.0", - "@salesforce/code-analyzer-eslint8-engine": "0.5.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", + "@salesforce/code-analyzer-eslint8-engine": "0.6.0-SNAPSHOT", "@salesforce/eslint-config-lwc": "^4.0.0", "@salesforce/eslint-plugin-lightning": "^2.0.0", "@types/node": "^20.0.0", @@ -77,4 +77,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-eslint8-engine/package.json b/packages/code-analyzer-eslint8-engine/package.json index 9d467d8d..73804633 100644 --- a/packages/code-analyzer-eslint8-engine/package.json +++ b/packages/code-analyzer-eslint8-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-eslint8-engine", "description": "Plugin package that adds 'eslint' (version 8) as an engine into Salesforce Code Analyzer", - "version": "0.5.0", + "version": "0.6.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -18,7 +18,7 @@ "@eslint/js": "8.57.1", "@lwc/eslint-plugin-lwc": "2.2.0", "@lwc/eslint-plugin-lwc-platform": "5.2.0", - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@salesforce/eslint-config-lwc": "3.7.2", "@salesforce/eslint-plugin-lightning": "1.0.1", "@types/node": "^20.0.0", @@ -75,4 +75,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-flow-engine/package.json b/packages/code-analyzer-flow-engine/package.json index 7a546391..c912fa73 100644 --- a/packages/code-analyzer-flow-engine/package.json +++ b/packages/code-analyzer-flow-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-flow-engine", "description": "Plugin package that adds 'Flow Scanner' as an engine into Salesforce Code Analyzer", - "version": "0.25.0", + "version": "0.26.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -63,4 +63,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-pmd-engine/package.json b/packages/code-analyzer-pmd-engine/package.json index c24ac80e..6bb28ca3 100644 --- a/packages/code-analyzer-pmd-engine/package.json +++ b/packages/code-analyzer-pmd-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-pmd-engine", "description": "Plugin package that adds 'pmd' and 'cpd' as engines into Salesforce Code Analyzer", - "version": "0.29.0", + "version": "0.30.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -71,4 +71,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-regex-engine/package.json b/packages/code-analyzer-regex-engine/package.json index 92fe4855..e874783f 100644 --- a/packages/code-analyzer-regex-engine/package.json +++ b/packages/code-analyzer-regex-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-regex-engine", "description": "Plugin package that adds 'regex' as an engine into Salesforce Code Analyzer", - "version": "0.26.0", + "version": "0.27.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "isbinaryfile": "^5.0.4", "p-limit": "^3.1.0" @@ -62,4 +62,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-retirejs-engine/package.json b/packages/code-analyzer-retirejs-engine/package.json index c681883a..ce130089 100644 --- a/packages/code-analyzer-retirejs-engine/package.json +++ b/packages/code-analyzer-retirejs-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-retirejs-engine", "description": "Plugin package that adds 'retire-js' as an engine into Salesforce Code Analyzer", - "version": "0.25.0", + "version": "0.26.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "isbinaryfile": "^5.0.4", "node-stream-zip": "^1.15.0", @@ -65,4 +65,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-sfge-engine/package.json b/packages/code-analyzer-sfge-engine/package.json index c367b111..2bd7a922 100644 --- a/packages/code-analyzer-sfge-engine/package.json +++ b/packages/code-analyzer-sfge-engine/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-sfge-engine", "description": "Plugin package that adds 'Salesforce Graph Engine' as an engine into Salesforce Code Analyzer", - "version": "0.11.0", + "version": "0.12.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.27.0", + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT", "@types/node": "^20.0.0", "semver": "^7.7.2" }, @@ -71,4 +71,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +} diff --git a/packages/code-analyzer-stylelint-engine/package.json b/packages/code-analyzer-stylelint-engine/package.json index 8c6c241a..84b6ad77 100644 --- a/packages/code-analyzer-stylelint-engine/package.json +++ b/packages/code-analyzer-stylelint-engine/package.json @@ -14,7 +14,7 @@ "types": "dist/index.d.ts", "dependencies": { "@types/node": "^20.0.0", - "@salesforce/code-analyzer-engine-api": "0.27.0" + "@salesforce/code-analyzer-engine-api": "0.28.0-SNAPSHOT" }, "devDependencies": { "@eslint/js": "^9.32.0", @@ -60,4 +60,4 @@ "!src/index.ts" ] } -} \ No newline at end of file +}