Skip to content

Commit

Permalink
JS-548 Make parser and parserOptions visible in rules (#5094)
Browse files Browse the repository at this point in the history
  • Loading branch information
vdiez authored Feb 4, 2025
1 parent f80b013 commit da0f473
Show file tree
Hide file tree
Showing 45 changed files with 294 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,6 @@
269
],
"ant-design:components/locale-provider/LocaleReceiver.tsx": [
15,
19,
33,
54,
Expand Down Expand Up @@ -3198,7 +3197,6 @@
],
"ant-design:components/transfer/list.tsx": [
15,
81,
87,
99,
121,
Expand Down
6 changes: 0 additions & 6 deletions its/ruling/src/test/expected/jsts/eigen/typescript-S1537.json
Original file line number Diff line number Diff line change
Expand Up @@ -13496,7 +13496,6 @@
],
"eigen:src/palette/elements/Select/Select.tsx": [
12,
46,
64,
149,
219,
Expand All @@ -13511,10 +13510,6 @@
204,
302
],
"eigen:src/palette/elements/Select/index.tsx": [
18,
22
],
"eigen:src/palette/elements/Separator/Separator.tsx": [
21
],
Expand Down Expand Up @@ -13749,7 +13744,6 @@
232
],
"eigen:src/storybook/helpers.tsx": [
5,
9,
26,
28,
Expand Down
4 changes: 2 additions & 2 deletions packages/html/tests/builder/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ import { embeddedInput } from '../../../jsts/tests/tools/helpers/input.js';
import { describe, it } from 'node:test';
import { expect } from 'expect';
import { parseHTML } from '../../src/parser/parse.js';
import { buildSourceCodes } from '../../../jsts/src/embedded/builder/build.js';
import { build } from '../../../jsts/src/embedded/builder/build.js';

describe('buildSourceCodes()', () => {
const fixturesPath = join(import.meta.dirname, 'fixtures');
it('should build source codes from an HTML file', async () => {
const filePath = join(fixturesPath, 'multiple.html');
const sourceCodes = buildSourceCodes(await embeddedInput({ filePath }), parseHTML);
const sourceCodes = build(await embeddedInput({ filePath }), parseHTML);
expect(sourceCodes).toHaveLength(2);
expect(sourceCodes[0].sourceCode.ast.loc.start).toEqual({ line: 4, column: 8 });
expect(sourceCodes[1].sourceCode.ast.loc.start).toEqual({ line: 8, column: 8 });
Expand Down
15 changes: 8 additions & 7 deletions packages/jsts/src/analysis/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { JsTsAnalysisInput, JsTsAnalysisOutput } from './analysis.js';
import type { TSESTree } from '@typescript-eslint/utils';
import { JsTsLanguage } from '../../../shared/src/helpers/language.js';
import { getLinter } from '../linter/linters.js';
import { buildSourceCode } from '../builders/build.js';
import { build } from '../builders/build.js';
import { LinterWrapper } from '../linter/wrapper.js';
import { APIError } from '../../../shared/src/errors/error.js';
import { serializeInProtobuf } from '../parsers/ast.js';
Expand All @@ -31,6 +31,7 @@ import { getSyntaxHighlighting } from '../linter/visitors/syntax-highlighting.js
import { getCpdTokens } from '../linter/visitors/cpd.js';
import { clearDependenciesCache, getAllDependencies } from '../rules/index.js';
import { Telemetry } from '../../../bridge/src/request.js';
import { ParseResult } from '../parsers/parse.js';

/**
* Analyzes a JavaScript / TypeScript analysis input
Expand All @@ -51,7 +52,7 @@ import { Telemetry } from '../../../bridge/src/request.js';
export function analyzeJSTS(input: JsTsAnalysisInput, language: JsTsLanguage): JsTsAnalysisOutput {
debug(`Analyzing file "${input.filePath}" with linterId "${input.linterId}"`);
const linter = getLinter(input.linterId);
return analyzeFile(linter, input, buildSourceCode(input, language));
return analyzeFile(linter, input, build(input, language));
}

/**
Expand All @@ -63,26 +64,26 @@ export function analyzeJSTS(input: JsTsAnalysisInput, language: JsTsLanguage): J
*
* @param linter the linter to use for the analysis
* @param input the JavaScript / TypeScript analysis input to analyze
* @param sourceCode the corresponding parsed ESLint SourceCode instance
* @param parseResult the corresponding parsing result containing the SourceCode instance
* @returns the JavaScript / TypeScript analysis output
*/
function analyzeFile(
linter: LinterWrapper,
input: JsTsAnalysisInput,
sourceCode: SourceCode,
parseResult: ParseResult,
): JsTsAnalysisOutput {
try {
const { filePath, fileType, language, shouldClearDependenciesCache } = input;
shouldClearDependenciesCache && clearDependenciesCache();
const { issues, highlightedSymbols, cognitiveComplexity, ucfgPaths } = linter.lint(
sourceCode,
parseResult,
filePath,
fileType,
language,
);
const extendedMetrics = computeExtendedMetrics(
input,
sourceCode,
parseResult.sourceCode,
highlightedSymbols,
cognitiveComplexity,
);
Expand All @@ -94,7 +95,7 @@ function analyzeFile(
};

if (!input.skipAst) {
const ast = serializeAst(sourceCode, filePath);
const ast = serializeAst(parseResult.sourceCode, filePath);
if (ast) {
result.ast = ast;
}
Expand Down
36 changes: 18 additions & 18 deletions packages/jsts/src/builders/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import { debug } from '../../../shared/src/helpers/logging.js';
import { JsTsAnalysisInput } from '../analysis/analysis.js';
import { buildParserOptions } from '../parsers/options.js';
import { parseForESLint } from '../parsers/parse.js';
import { parsers } from '../parsers/eslint.js';
import { parse } from '../parsers/parse.js';
import { Parser, parsersMap } from '../parsers/eslint.js';
import { getProgramById } from '../program/program.js';
import { Linter } from 'eslint';
import { JsTsLanguage } from '../../../shared/src/helpers/language.js';
Expand All @@ -34,59 +34,59 @@ import { getContext } from '../../../shared/src/helpers/context.js';
* @param language the language of the input
* @returns the parsed source code
*/
export function buildSourceCode(input: JsTsAnalysisInput, language: JsTsLanguage) {
export function build(input: JsTsAnalysisInput, language: JsTsLanguage) {
const vueFile = isVueFile(input.filePath);

let parser: Parser = vueFile ? parsersMap.vuejs : parsersMap.typescript;
if (shouldUseTypescriptParser(language)) {
const options: Linter.ParserOptions = {
// enable logs for @typescript-eslint
// debugLevel: true,
filePath: input.filePath,
parser: vueFile ? parsers.typescript : undefined,
parser: vueFile ? parsersMap.typescript : undefined,
};
const parser = vueFile ? parsers.vuejs : parsers.typescript;
if (!vueFile) {
options.programs = input.programId && [getProgramById(input.programId)];
options.project = input.tsConfigs;
}
try {
debug(`Parsing ${input.filePath} with ${parser.parser}`);
return parseForESLint(input.fileContent, parser.parse, buildParserOptions(options, false));
debug(`Parsing ${input.filePath} with ${parser.meta.name}`);
return parse(input.fileContent, parser, buildParserOptions(options, false));
} catch (error) {
debug(`Failed to parse ${input.filePath} with TypeScript parser: ${error.message}`);
debug(`Failed to parse ${input.filePath} with ${parser.meta.name}: ${error.message}`);
if (language === 'ts') {
throw error;
}
}
}

let moduleError;
parser = vueFile ? parsersMap.vuejs : parsersMap.javascript;
try {
const parser = vueFile ? parsers.vuejs : parsers.javascript;
debug(`Parsing ${input.filePath} with ${parser.parser}`);
return parseForESLint(
debug(`Parsing ${input.filePath} with ${parser.meta?.name}`);
return parse(
input.fileContent,
parser.parse,
buildParserOptions({ parser: vueFile ? parsers.javascript : undefined }, true),
parser,
buildParserOptions({ parser: vueFile ? parsersMap.javascript : undefined }, true),
);
} catch (error) {
debug(`Failed to parse ${input.filePath} with Javascript parser: ${error.message}`);
debug(`Failed to parse ${input.filePath} with ${parser.meta?.name}: ${error.message}`);
if (vueFile) {
throw error;
}
moduleError = error;
}

try {
debug(`Parsing ${input.filePath} with Javascript parser in 'script' mode`);
return parseForESLint(
debug(`Parsing ${input.filePath} with ${parsersMap.javascript.meta?.name} in 'script' mode`);
return parse(
input.fileContent,
parsers.javascript.parse,
parsersMap.javascript,
buildParserOptions({ sourceType: 'script' }, true),
);
} catch (error) {
debug(
`Failed to parse ${input.filePath} with Javascript parser in 'script' mode: ${error.message}`,
`Failed to parse ${input.filePath} with ${parsersMap.javascript.meta?.name} in 'script' mode: ${error.message}`,
);
/**
* We prefer displaying parsing error as module if parsing as script also failed,
Expand Down
24 changes: 12 additions & 12 deletions packages/jsts/src/embedded/analysis/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { getLinter } from '../../linter/linters.js';
import type { LinterWrapper } from '../../linter/wrapper.js';
import { EmbeddedAnalysisInput, EmbeddedAnalysisOutput } from './analysis.js';
import { findNcloc } from '../../linter/visitors/metrics/ncloc.js';
import { buildSourceCodes, ExtendedSourceCode, LanguageParser } from '../builder/build.js';
import { build, ExtendedParseResult, LanguageParser } from '../builder/build.js';
import { debug } from '../../../../shared/src/helpers/logging.js';

/**
Expand Down Expand Up @@ -51,25 +51,25 @@ export function analyzeEmbedded(
): EmbeddedAnalysisOutput {
debug(`Analyzing file "${input.filePath}" with linterId "${input.linterId}"`);
const linter = getLinter(input.linterId);
const extendedSourceCodes = buildSourceCodes(input, languageParser);
return analyzeFile(linter, extendedSourceCodes);
const extendedParseResults = build(input, languageParser);
return analyzeFile(linter, extendedParseResults);
}

/**
* Extracted logic from analyzeEmbedded() so we can compute metrics
*
* @param linter
* @param extendedSourceCodes
* @param extendedParseResults
* @returns
*/
function analyzeFile(linter: LinterWrapper, extendedSourceCodes: ExtendedSourceCode[]) {
function analyzeFile(linter: LinterWrapper, extendedParseResults: ExtendedParseResult[]) {
const aggregatedIssues: Issue[] = [];
const aggregatedUcfgPaths: string[] = [];
let ncloc: number[] = [];
for (const extendedSourceCode of extendedSourceCodes) {
const { issues, ucfgPaths, ncloc: singleNcLoc } = analyzeSnippet(linter, extendedSourceCode);
for (const extendedParseResult of extendedParseResults) {
const { issues, ucfgPaths, ncloc: singleNcLoc } = analyzeSnippet(linter, extendedParseResult);
ncloc = ncloc.concat(singleNcLoc);
const filteredIssues = removeNonJsIssues(extendedSourceCode.sourceCode, issues);
const filteredIssues = removeNonJsIssues(extendedParseResult.sourceCode, issues);
aggregatedIssues.push(...filteredIssues);
aggregatedUcfgPaths.push(...ucfgPaths);
}
Expand All @@ -79,13 +79,13 @@ function analyzeFile(linter: LinterWrapper, extendedSourceCodes: ExtendedSourceC
metrics: { ncloc },
};

function analyzeSnippet(linter: LinterWrapper, extendedSourceCode: ExtendedSourceCode) {
function analyzeSnippet(linter: LinterWrapper, extendedParseResult: ExtendedParseResult) {
const { issues, ucfgPaths } = linter.lint(
extendedSourceCode.sourceCode,
extendedSourceCode.syntheticFilePath,
extendedParseResult,
extendedParseResult.syntheticFilePath,
'MAIN',
);
const ncloc = findNcloc(extendedSourceCode.sourceCode);
const ncloc = findNcloc(extendedParseResult.sourceCode);
return { issues, ucfgPaths, ncloc };
}

Expand Down
27 changes: 14 additions & 13 deletions packages/jsts/src/embedded/builder/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
import { SourceCode } from 'eslint';
import { patchParsingError, patchSourceCode } from './patch.js';
import path from 'path';
import { EmbeddedJS } from '../analysis/embedded-js.js';
import { EmbeddedAnalysisInput } from '../analysis/analysis.js';
import { JsTsAnalysisInput } from '../../analysis/analysis.js';
import { buildSourceCode } from '../../builders/build.js';
import { build as buildJsTs } from '../../builders/build.js';
import { ParseResult } from '../../parsers/parse.js';

export type ExtendedSourceCode = {
sourceCode: SourceCode;
export type ExtendedParseResult = ParseResult & {
syntheticFilePath: string;
};
export type LanguageParser = (text: string) => EmbeddedJS[];
Expand All @@ -37,12 +36,12 @@ export type LanguageParser = (text: string) => EmbeddedJS[];
* If there is at least one parsing error in any snippet, we return only the first error and
* we don't even consider any parsing errors in the remaining snippets for simplicity.
*/
export function buildSourceCodes(
export function build(
input: EmbeddedAnalysisInput,
languageParser: LanguageParser,
): ExtendedSourceCode[] {
): ExtendedParseResult[] {
const embeddedJSs: EmbeddedJS[] = languageParser(input.fileContent);
const extendedSourceCodes: ExtendedSourceCode[] = [];
const extendedParseResults: ExtendedParseResult[] = [];
for (const embeddedJS of embeddedJSs) {
const { code } = embeddedJS;

Expand All @@ -62,22 +61,24 @@ export function buildSourceCodes(
fileType: 'MAIN',
} as JsTsAnalysisInput;
try {
const extendedSourceCode = {
sourceCode: patchSourceCode(buildSourceCode(jsTsAnalysisInput, 'js'), embeddedJS),
const parseResult = buildJsTs(jsTsAnalysisInput, 'js');
extendedParseResults.push({
sourceCode: patchSourceCode(parseResult.sourceCode, embeddedJS),
parser: parseResult.parser,
parserOptions: parseResult.parserOptions,
syntheticFilePath,
};
extendedSourceCodes.push(extendedSourceCode);
});
} catch (error) {
throw patchParsingError(error, embeddedJS);
}
}
return extendedSourceCodes;
return extendedParseResults;
}

/**
* Returns the filename composed as following:
*
* {filepath-without-extention}-{resourceName}{filepath-extension}
* {filepath-without-extension}-{resourceName}{filepath-extension}
*/
export function composeSyntheticFilePath(filePath: string, resourceName: string): string {
const { dir, name, ext } = path.parse(filePath);
Expand Down
Loading

0 comments on commit da0f473

Please sign in to comment.