Skip to content

Commit 2233eec

Browse files
committed
JS-554 Refactor JS ruling
1 parent 85feb29 commit 2233eec

File tree

104 files changed

+733
-1166
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+733
-1166
lines changed

packages/bridge/src/delegate.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,29 @@ import { JsTsAnalysisOutputWithAst } from '../../jsts/src/analysis/analysis.js';
2121
import { handleRequest } from './handle-request.js';
2222
import { AnalysisOutput } from '../../shared/src/types/analysis.js';
2323
import { RequestResult, RequestType } from './request.js';
24+
import { WorkerData } from '../../shared/src/helpers/worker.js';
2425

2526
/**
2627
* Returns a delegate function to handle an HTTP request
2728
*/
28-
export function createDelegator(worker: Worker | undefined) {
29+
export function createDelegator(worker: Worker | undefined, workerData?: WorkerData) {
2930
return function (type: RequestType) {
30-
return worker ? createWorkerHandler(worker, type) : createHandler(type);
31+
return worker ? createWorkerHandler(worker, type) : createHandler(type, workerData);
3132
};
3233
}
3334

3435
/**
3536
* Handler to analyze in the same thread as HTTP server. Used for testing purposes
3637
* @param type
38+
* @param workerData
3739
*/
38-
function createHandler(type: RequestType) {
40+
function createHandler(type: RequestType, workerData?: WorkerData) {
3941
return async (
4042
request: express.Request,
4143
response: express.Response,
4244
next: express.NextFunction,
4345
) => {
44-
handleResult(await handleRequest({ type, data: request.body }), response, next);
46+
handleResult(await handleRequest({ type, data: request.body }, workerData), response, next);
4547
};
4648
}
4749

packages/bridge/src/errors/middleware.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import express from 'express';
1818
import { ErrorCode } from '../../../shared/src/errors/error.js';
1919
import { error } from '../../../shared/src/helpers/logging.js';
20-
import { JsTsAnalysisOutput } from '../../../jsts/src/analysis/analysis.js';
2120

2221
/**
2322
* Express.js middleware for handling error while serving requests.
@@ -67,28 +66,21 @@ export function parseParsingError(error: {
6766
code: error.code,
6867
line: error.data?.line,
6968
},
70-
...EMPTY_JSTS_ANALYSIS_OUTPUT,
7169
};
7270
}
7371

7472
/**
75-
* An empty JavaScript / TypeScript analysis output sent back on paring errors.
73+
* Creates a S2260 issue from the parsing error
7674
*/
77-
export const EMPTY_JSTS_ANALYSIS_OUTPUT: JsTsAnalysisOutput = {
78-
issues: [],
79-
highlights: [],
80-
highlightedSymbols: [],
81-
metrics: {
82-
ncloc: [],
83-
commentLines: [],
84-
nosonarLines: [],
85-
executableLines: [],
86-
functions: 0,
87-
statements: 0,
88-
classes: 0,
89-
complexity: 0,
90-
cognitiveComplexity: 0,
91-
},
92-
cpdTokens: [],
93-
ast: new Uint8Array(),
94-
};
75+
export function createParsingIssue({
76+
parsingError: { line, message },
77+
}: {
78+
parsingError: { line?: number; message: string };
79+
}) {
80+
return {
81+
language: 'js',
82+
ruleId: 'S2260',
83+
line,
84+
message,
85+
} as const;
86+
}

packages/bridge/src/handle-request.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,40 @@ import {
2828
} from '../../jsts/src/program/program.js';
2929
import { Linter } from '../../jsts/src/linter/linter.js';
3030
import { clearTypeScriptESLintParserCaches } from '../../jsts/src/parsers/eslint.js';
31-
import {
32-
BridgeRequest,
33-
fillLanguage,
34-
readFileLazily,
35-
RequestResult,
36-
serializeError,
37-
} from './request.js';
31+
import { BridgeRequest, RequestResult, serializeError } from './request.js';
32+
import { WorkerData } from '../../shared/src/helpers/worker.js';
3833

39-
export async function handleRequest(request: BridgeRequest): Promise<RequestResult> {
34+
export async function handleRequest(
35+
request: BridgeRequest,
36+
workerData?: WorkerData,
37+
): Promise<RequestResult> {
4038
try {
4139
switch (request.type) {
4240
case 'on-init-linter': {
4341
await Linter.initialize(request.data);
4442
return { type: 'success', result: 'OK!' };
4543
}
4644
case 'on-analyze-jsts': {
47-
const output = analyzeJSTS(fillLanguage(await readFileLazily(request.data)));
45+
const output = await analyzeJSTS(request.data);
4846
return {
4947
type: 'success',
5048
result: output,
5149
};
5250
}
5351
case 'on-create-program': {
54-
logHeapStatistics();
52+
logHeapStatistics(workerData?.debugMemory);
5553
const { programId, files, projectReferences, missingTsConfig } = createAndSaveProgram(
5654
request.data.tsConfig,
5755
);
56+
logHeapStatistics(workerData?.debugMemory);
5857
return {
5958
type: 'success',
6059
result: { programId, files, projectReferences, missingTsConfig },
6160
};
6261
}
6362
case 'on-delete-program': {
6463
deleteProgram(request.data.programId);
65-
logHeapStatistics();
64+
logHeapStatistics(workerData?.debugMemory);
6665
return { type: 'success', result: 'OK!' };
6766
}
6867
case 'on-create-tsconfig-file': {
@@ -88,16 +87,16 @@ export async function handleRequest(request: BridgeRequest): Promise<RequestResu
8887
};
8988
}
9089
case 'on-analyze-css': {
91-
const output = await analyzeCSS(await readFileLazily(request.data));
90+
const output = await analyzeCSS(request.data);
9291
return { type: 'success', result: output };
9392
}
9493
case 'on-analyze-yaml': {
95-
const output = analyzeYAML(await readFileLazily(request.data));
94+
const output = await analyzeYAML(request.data);
9695
return { type: 'success', result: output };
9796
}
9897

9998
case 'on-analyze-html': {
100-
const output = analyzeHTML(await readFileLazily(request.data));
99+
const output = await analyzeHTML(request.data);
101100
return { type: 'success', result: output };
102101
}
103102
case 'on-analyze-project': {

packages/bridge/src/memory.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { constants, NodeGCPerformanceDetail, PerformanceObserver } from 'perf_ho
2121
import { debug, error, info, warn } from '../../shared/src/helpers/logging.js';
2222

2323
const MB = 1024 * 1024;
24-
let debugMemory = false;
2524

2625
export function logMemoryConfiguration() {
2726
const osMem = Math.floor(os.totalmem() / MB);
@@ -83,7 +82,6 @@ export function logMemoryError(err: any) {
8382
}
8483

8584
export function registerGarbageCollectionObserver() {
86-
debugMemory = true;
8785
const obs = new PerformanceObserver(items => {
8886
items
8987
.getEntries()
@@ -94,13 +92,13 @@ export function registerGarbageCollectionObserver() {
9492
.forEach(item => {
9593
debug(`Major GC event`);
9694
debug(JSON.stringify(item));
97-
logHeapStatistics();
95+
logHeapStatistics(true);
9896
});
9997
});
10098
obs.observe({ entryTypes: ['gc'] });
10199
}
102100

103-
export function logHeapStatistics() {
101+
export function logHeapStatistics(debugMemory = false) {
104102
if (debugMemory) {
105103
debug(JSON.stringify(v8.getHeapStatistics()));
106104
}

packages/bridge/src/request.ts

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515
* along with this program; if not, see https://sonarsource.com/license/ssal/
1616
*/
1717
import { AnalysisOutput } from '../../shared/src/types/analysis.js';
18-
import { CssAnalysisInput } from '../../css/src/analysis/analysis.js';
19-
import { EmbeddedAnalysisInput } from '../../jsts/src/embedded/analysis/analysis.js';
20-
import { JsTsAnalysisInput } from '../../jsts/src/analysis/analysis.js';
2118
import { ProjectAnalysisInput } from '../../jsts/src/analysis/projectAnalysis/projectAnalysis.js';
2219
import { TsConfigJson } from 'type-fest';
2320
import { RuleConfig } from '../../jsts/src/linter/config/rule-config.js';
24-
import { readFile } from '../../shared/src/helpers/files.js';
2521
import { APIError, ErrorCode } from '../../shared/src/errors/error.js';
2622
import { NamedDependency } from '../../jsts/src/rules/index.js';
27-
import { JsTsLanguage, isJsFile, isTsFile } from '../../shared/src/helpers/language.js';
23+
import { CssAnalysisInput } from '../../css/src/analysis/analysis.js';
24+
import { JsTsAnalysisInput } from '../../jsts/src/analysis/analysis.js';
25+
import { EmbeddedAnalysisInput } from '../../jsts/src/embedded/analysis/analysis.js';
2826

2927
export type RequestResult =
3028
| {
@@ -42,22 +40,6 @@ export type Telemetry = {
4240

4341
export type RequestType = BridgeRequest['type'];
4442

45-
type MaybeIncompleteCssAnalysisInput = Omit<CssAnalysisInput, 'fileContent'> & {
46-
fileContent?: string;
47-
};
48-
type MaybeIncompleteJsTsAnalysisInput = Omit<JsTsAnalysisInput, 'fileContent' | 'language'> & {
49-
fileContent?: string;
50-
language?: JsTsLanguage;
51-
};
52-
type MaybeIncompleteEmbeddedAnalysisInput = Omit<EmbeddedAnalysisInput, 'fileContent'> & {
53-
fileContent?: string;
54-
};
55-
56-
type MaybeIncompleteAnalysisInput =
57-
| MaybeIncompleteJsTsAnalysisInput
58-
| MaybeIncompleteCssAnalysisInput
59-
| MaybeIncompleteEmbeddedAnalysisInput;
60-
6143
export type BridgeRequest =
6244
| CssRequest
6345
| JsTsRequest
@@ -73,17 +55,17 @@ export type BridgeRequest =
7355

7456
type CssRequest = {
7557
type: 'on-analyze-css';
76-
data: MaybeIncompleteCssAnalysisInput;
58+
data: CssAnalysisInput;
7759
};
7860

7961
type EmbeddedRequest = {
8062
type: 'on-analyze-html' | 'on-analyze-yaml';
81-
data: MaybeIncompleteEmbeddedAnalysisInput;
63+
data: EmbeddedAnalysisInput;
8264
};
8365

8466
type JsTsRequest = {
8567
type: 'on-analyze-jsts';
86-
data: MaybeIncompleteJsTsAnalysisInput;
68+
data: JsTsAnalysisInput;
8769
};
8870

8971
type ProjectAnalysisRequest = {
@@ -129,51 +111,6 @@ type GetTelemetryRequest = {
129111
type: 'on-get-telemetry';
130112
};
131113

132-
/**
133-
* In SonarQube context, an analysis input includes both path and content of a file
134-
* to analyze. However, in SonarLint, we might only get the file path. As a result,
135-
* we read the file if the content is missing in the input.
136-
*/
137-
export async function readFileLazily<T extends MaybeIncompleteAnalysisInput>(
138-
input: T,
139-
): Promise<T & { fileContent: string }> {
140-
if (!isCompleteAnalysisInput(input)) {
141-
return {
142-
...input,
143-
fileContent: await readFile(input.filePath),
144-
};
145-
}
146-
return input;
147-
}
148-
149-
/**
150-
* In SonarQube context, an analysis input includes both path and content of a file
151-
* to analyze. However, in SonarLint, we might only get the file path. As a result,
152-
* we read the file if the content is missing in the input.
153-
*/
154-
export function fillLanguage<T extends MaybeIncompleteJsTsAnalysisInput & { fileContent: string }>(
155-
input: T,
156-
): JsTsAnalysisInput {
157-
if (isTsFile(input.filePath, input.fileContent)) {
158-
return {
159-
...input,
160-
language: 'ts',
161-
};
162-
} else if (isJsFile(input.filePath)) {
163-
return {
164-
...input,
165-
language: 'js',
166-
};
167-
}
168-
throw new Error(`Unable to find language for file ${input.filePath}`);
169-
}
170-
171-
export function isCompleteAnalysisInput<T extends MaybeIncompleteAnalysisInput>(
172-
input: T,
173-
): input is T & { fileContent: string } {
174-
return 'fileContent' in input;
175-
}
176-
177114
/**
178115
* The default (de)serialization mechanism of the Worker Thread API cannot be used
179116
* to (de)serialize Error instances. To address this, we turn those instances into

packages/bridge/src/router.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
import * as express from 'express';
1818
import { Worker } from 'worker_threads';
1919
import { createDelegator } from './delegate.js';
20+
import { WorkerData } from '../../shared/src/helpers/worker.js';
2021

21-
export default function (worker?: Worker): express.Router {
22+
export default function (worker: Worker | undefined, workerData?: WorkerData): express.Router {
2223
const router = express.Router();
23-
const delegate = createDelegator(worker);
24+
const delegate = createDelegator(worker, workerData);
2425

2526
/** Endpoints running on the worker thread */
2627
router.post('/analyze-project', delegate('on-analyze-project'));

packages/bridge/src/server.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import {
3030
registerGarbageCollectionObserver,
3131
logMemoryConfiguration,
3232
logMemoryError,
33-
logHeapStatistics,
3433
} from './memory.js';
3534

3635
/**
@@ -84,7 +83,6 @@ export function start(
8483
if (debugMemory) {
8584
registerGarbageCollectionObserver();
8685
}
87-
logHeapStatistics();
8886
return new Promise(resolve => {
8987
debug('Starting the bridge server');
9088

@@ -113,7 +111,7 @@ export function start(
113111
*/
114112
app.use(express.json({ limit: MAX_REQUEST_SIZE }));
115113
app.use(orphanTimeout.middleware);
116-
app.use(router(worker));
114+
app.use(router(worker, { debugMemory }));
117115
app.use(errorMiddleware);
118116

119117
app.post('/close', (_: express.Request, response: express.Response) => {

packages/bridge/src/worker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* You should have received a copy of the Sonar Source-Available License
1515
* along with this program; if not, see https://sonarsource.com/license/ssal/
1616
*/
17-
import { parentPort } from 'worker_threads';
17+
import { parentPort, workerData } from 'worker_threads';
1818
import { handleRequest } from './handle-request.js';
1919
import { BridgeRequest } from './request.js';
2020

@@ -28,7 +28,7 @@ if (parentPort) {
2828
if (type === 'close') {
2929
parentThread.close();
3030
} else {
31-
parentThread.postMessage(await handleRequest(message));
31+
parentThread.postMessage(await handleRequest(message, workerData));
3232
}
3333
});
3434
}

packages/bridge/tests/errors/middleware.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* along with this program; if not, see https://sonarsource.com/license/ssal/
1616
*/
1717
import express from 'express';
18-
import { EMPTY_JSTS_ANALYSIS_OUTPUT, errorMiddleware } from '../../src/errors/index.js';
18+
import { errorMiddleware } from '../../src/errors/index.js';
1919
import assert from 'assert';
2020

2121
import { describe, it, beforeEach, mock, Mock } from 'node:test';
@@ -47,7 +47,6 @@ describe('errorMiddleware', () => {
4747
line: 42,
4848
code: ErrorCode.Parsing,
4949
},
50-
...EMPTY_JSTS_ANALYSIS_OUTPUT,
5150
},
5251
);
5352
});

packages/bridge/tests/router.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('router', () => {
6666
],
6767
baseDir: fixtures,
6868
files: {
69-
[filePath]: { fileType: 'MAIN' },
69+
[filePath]: { fileType: 'MAIN', filePath },
7070
},
7171
};
7272

0 commit comments

Comments
 (0)