Skip to content

Commit

Permalink
fix: Check unmatched patterns (#334)
Browse files Browse the repository at this point in the history
JIRA: CPOUI5FOUNDATION-881

---------

Co-authored-by: Merlin Beutlberger <m.beutlberger@sap.com>
  • Loading branch information
d3xter666 and RandomByte authored Oct 1, 2024
1 parent 74ad824 commit 329f2cd
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 14 deletions.
4 changes: 3 additions & 1 deletion src/linter/lintWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {UI5LintConfigType} from "../utils/ConfigManager.js";

export default async function lintWorkspace(
workspace: AbstractAdapter, filePathsWorkspace: AbstractAdapter,
options: LinterOptions, config: UI5LintConfigType
options: LinterOptions, config: UI5LintConfigType, patternsMatch: Set<string>
): Promise<LintResult[]> {
const done = taskStart("Linting Workspace");

Expand All @@ -29,13 +29,15 @@ export default async function lintWorkspace(
}),
inverseResult: true,
namespace: options.namespace,
patternsMatch,
});
reader = await resolveReader({
patterns: options.ignorePattern ?? [],
projectRootDir: options.rootDir,
ui5ConfigPath: config.ui5Config,
resourceReader: reader,
namespace: options.namespace,
patternsMatch,
});
context.setRootReader(reader);

Expand Down
45 changes: 40 additions & 5 deletions src/linter/linter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {graphFromObject} from "@ui5/project/graph";
import {createReader, createWorkspace, createReaderCollection, createFilterReader} from "@ui5/fs/resourceFactory";
import {FilePath, LinterOptions, LintResult} from "./LinterContext.js";
import {FilePath, FilePattern, LinterOptions, LintResult} from "./LinterContext.js";
import lintWorkspace from "./lintWorkspace.js";
import {taskStart} from "../utils/perf.js";
import path from "node:path";
Expand All @@ -11,6 +11,11 @@ import type {AbstractReader, Resource} from "@ui5/fs";
import ConfigManager, {UI5LintConfigType} from "../utils/ConfigManager.js";
import {Minimatch} from "minimatch";

// Internal analytical variable that will store matchers' count.
// We cannot predict the outcome of the matchers, so stash usage statistics
// and later analyze the results from it.
const matchedPatterns = new Set<string>();

export async function lintProject({
rootDir, filePatterns, ignorePattern, reportCoverage, includeMessageDetails, configPath, ui5ConfigPath,
}: LinterOptions): Promise<LintResult[]> {
Expand Down Expand Up @@ -131,6 +136,7 @@ async function lint(
projectRootDir: rootDir,
resourceReader,
namespace: options.namespace,
patternsMatch: matchedPatterns,
});

// Apply files + ignores over the filePaths reader
Expand All @@ -140,20 +146,24 @@ async function lint(
resourceReader,
inverseResult: true,
namespace: options.namespace,
patternsMatch: matchedPatterns,
});
filePathsReader = await resolveReader({
patterns: ignorePattern,
projectRootDir: rootDir,
resourceReader: filePathsReader,
namespace: options.namespace,
patternsMatch: matchedPatterns,
});
const filePathsWorkspace = createWorkspace({reader: filePathsReader});

const workspace = createWorkspace({
reader,
});

const res = await lintWorkspace(workspace, filePathsWorkspace, options, config);
const res = await lintWorkspace(workspace, filePathsWorkspace, options, config, matchedPatterns);
checkUnmatchedPatterns(filePatterns, matchedPatterns);

lintEnd();
return res;
}
Expand Down Expand Up @@ -257,13 +267,15 @@ function sortLintResults(lintResults: LintResult[]) {
lintResults.sort((a, b) => a.filePath.localeCompare(b.filePath));
}

function isFileIncluded(file: string, patterns: Minimatch[]) {
function isFileIncluded(file: string, patterns: Minimatch[], patternsMatch: Set<string>) {
let include = true;

for (const pattern of patterns) {
if (pattern.negate && pattern.match(file)) {
patternsMatch.add(pattern.pattern);
include = true; // re-include it
} else if (pattern.match(file)) { // Handle inclusion: exclude if it matches
patternsMatch.add(pattern.pattern);
include = false;
}
}
Expand Down Expand Up @@ -296,13 +308,15 @@ export async function resolveReader({
namespace,
ui5ConfigPath,
inverseResult = false,
patternsMatch,
}: {
patterns: string[];
projectRootDir: string;
resourceReader: AbstractReader;
namespace?: string;
ui5ConfigPath?: string;
inverseResult?: boolean;
patternsMatch: Set<string>;
}) {
if (!patterns.length) {
return resourceReader;
Expand Down Expand Up @@ -346,8 +360,29 @@ export async function resolveReader({
return inverseResult ?
// When we work with files paths we actually need to limit the result to those
// matches, instead of allowing all except XYZ
!isFileIncluded(resPath, minimatchPatterns) :
isFileIncluded(resPath, minimatchPatterns);
!isFileIncluded(resPath, minimatchPatterns, patternsMatch) :
isFileIncluded(resPath, minimatchPatterns, patternsMatch);
},
});
}

/**
* Checks which patterns were not matched during analysis
*
* @param patterns Available patterns
* @throws Error if an unmatched pattern is found
*/
function checkUnmatchedPatterns(patterns: FilePattern[], patternsMatch: Set<string>) {
const unmatchedPatterns = patterns.reduce((acc, pattern) => {
if (!patternsMatch.has(pattern)) {
acc.push(pattern);
}

return acc;
}, [] as FilePattern[]);

if (unmatchedPatterns.length) {
throw new Error(`Specified file ${unmatchedPatterns.length === 1 ? "pattern" : "patterns"}` +
` '${unmatchedPatterns.join("', '")}' did not match any resource`);
}
}
5 changes: 3 additions & 2 deletions src/utils/ConfigManager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import path, {dirname} from "node:path";
import {fileURLToPath} from "node:url";
import {FilePattern} from "../linter/LinterContext.js";
const __dirname = dirname(fileURLToPath(import.meta.url));

export interface UI5LintConfigType {
files?: string[];
ignores?: string[];
files?: FilePattern[];
ignores?: FilePattern[];
ui5Config?: string;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default {
files: [
"webapp/**/*",
"unmatched-pattern1",
"unmatched-pattern2",
"unmatched-pattern3",
],
ignores: [
"test/**/*",
"!test/sap/m/visual/Wizard.spec.js",
],
};

This file was deleted.

This file was deleted.

14 changes: 14 additions & 0 deletions test/lib/linter/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ test.serial("lint: One file of com.ui5.troublesome.app (without details / covera
t.snapshot(preprocessLintResultsForSnapshot(res));
});

test.serial("lint: com.ui5.troublesome.app with unmatched patterns", async (t) => {
const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app");

const {lintProject} = t.context;

await t.throwsAsync(lintProject({
rootDir: projectPath,
configPath: "ui5lint.config.unmatched-patterns.mjs",
}), {
message: `Specified file patterns 'unmatched-pattern1', ` +
`'unmatched-pattern2', 'unmatched-pattern3' did not match any resource`,
});
});

test.serial("lint: All files of library.with.custom.paths", async (t) => {
const projectPath = path.join(fixturesProjectsPath, "library.with.custom.paths");
const {lintProject} = t.context;
Expand Down

0 comments on commit 329f2cd

Please sign in to comment.