Skip to content

Commit daef594

Browse files
Merge pull request #11 from ValentinVignal/support-nested-analysis-options
feat: Support nested analysis options
2 parents 852844f + 8ec1fb6 commit daef594

5 files changed

Lines changed: 162 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.16
2+
3+
- :sparkles: Supports nested analysis options.
4+
15
## 0.15
26

37
- :arrow_up: Upgrade to node 16.

dist/index.js

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17090,29 +17090,78 @@ var __importStar = (this && this.__importStar) || function (mod) {
1709017090
__setModuleDefault(result, mod);
1709117091
return result;
1709217092
};
17093+
var __importDefault = (this && this.__importDefault) || function (mod) {
17094+
return (mod && mod.__esModule) ? mod : { "default": mod };
17095+
};
1709317096
Object.defineProperty(exports, "__esModule", ({ value: true }));
1709417097
exports.IgnoredFiles = void 0;
1709517098
const fs = __importStar(__nccwpck_require__(5747));
1709617099
const yaml = __importStar(__nccwpck_require__(1917));
17097-
const minimatch = __importStar(__nccwpck_require__(3973));
17100+
const minimatch_1 = __importDefault(__nccwpck_require__(3973));
1709817101
const path = __importStar(__nccwpck_require__(5622));
1709917102
const ActionOptions_1 = __nccwpck_require__(3615);
1710017103
/**
1710117104
* The ignore files in the analysis_options.yaml
1710217105
*/
1710317106
class IgnoredFiles {
1710417107
constructor() {
17105-
var _a, _b;
1710617108
let patterns;
1710717109
try {
17108-
const yamlFile = yaml.load(fs.readFileSync(path.resolve(ActionOptions_1.actionOptions.workingDirectory, 'analysis_options.yaml'), 'utf8'));
17109-
patterns = (_b = (_a = yamlFile === null || yamlFile === void 0 ? void 0 : yamlFile.analyzer) === null || _a === void 0 ? void 0 : _a.exclude) !== null && _b !== void 0 ? _b : [];
17110+
const yamlPath = IgnoredFiles.findClosestYamlFile(ActionOptions_1.actionOptions.workingDirectory);
17111+
if (!yamlPath) {
17112+
throw new Error(`Could not find any "analysis_options.yaml" in the parent directories of "${ActionOptions_1.actionOptions.workingDirectory}"`);
17113+
}
17114+
patterns = IgnoredFiles.getIgnoredPatterns(yamlPath);
1711017115
}
1711117116
catch (error) {
17112-
console.log('Could not load analysis_options.yaml:\n', error);
17117+
console.error('Could not load analysis_options.yaml:\n', error);
1711317118
}
1711417119
patterns !== null && patterns !== void 0 ? patterns : (patterns = []);
17115-
this.patterns = patterns.map((pattern) => new minimatch.Minimatch(pattern));
17120+
this.patterns = patterns.map((pattern) => new minimatch_1.default.Minimatch(pattern));
17121+
}
17122+
/**
17123+
*
17124+
* @param path
17125+
*/
17126+
static findClosestYamlFile(directoryPath) {
17127+
const yamlPath = path.resolve(directoryPath, 'analysis_options.yaml');
17128+
if (fs.existsSync(yamlPath)) {
17129+
return yamlPath;
17130+
}
17131+
else {
17132+
const parentDirectoryPath = path.resolve(directoryPath, '..');
17133+
if (parentDirectoryPath === directoryPath) {
17134+
return null;
17135+
}
17136+
else {
17137+
return IgnoredFiles.findClosestYamlFile(parentDirectoryPath);
17138+
}
17139+
}
17140+
}
17141+
static getIgnoredPatterns(yamlPath) {
17142+
var _a;
17143+
const yamlFile = yaml.load(fs.readFileSync(yamlPath, 'utf8'));
17144+
const exclude = (_a = yamlFile === null || yamlFile === void 0 ? void 0 : yamlFile.analyzer) === null || _a === void 0 ? void 0 : _a.exclude;
17145+
let patterns;
17146+
if (exclude) {
17147+
if (Array.isArray(exclude)) {
17148+
patterns = exclude;
17149+
}
17150+
else if (typeof exclude === 'string') {
17151+
patterns = [exclude];
17152+
}
17153+
}
17154+
patterns !== null && patterns !== void 0 ? patterns : (patterns = []);
17155+
if (yamlFile === null || yamlFile === void 0 ? void 0 : yamlFile.include) {
17156+
const newPath = path.resolve(path.dirname(yamlPath), yamlFile.include);
17157+
if (fs.existsSync(newPath)) {
17158+
return [
17159+
...IgnoredFiles.getIgnoredPatterns(newPath),
17160+
...patterns,
17161+
];
17162+
}
17163+
}
17164+
return patterns;
1711617165
}
1711717166
/**
1711817167
* Whether a file is ignored

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "action-dart-analyze",
3-
"version": "0.15.0",
3+
"version": "0.16.0",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

src/utils/IgnoredFiles.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ jest.mock('path', () => mockPath);
1616

1717
const mockFs = {
1818
readFileSync: jest.fn(),
19+
existsSync: jest.fn(),
1920
};
2021

22+
2123
jest.mock('fs', () => mockFs);
2224

2325
const mockYaml = {
@@ -44,6 +46,7 @@ describe('IgnoredFiles', () => {
4446
} as ActionOptions;
4547

4648
mockPath.resolve.mockReturnValue('path/to/yaml');
49+
mockFs.existsSync.mockReturnValue(true);
4750
mockFs.readFileSync.mockReturnValue('yamlContent');
4851
mockYaml.load.mockReturnValue({
4952
analyzer: {
@@ -65,6 +68,48 @@ describe('IgnoredFiles', () => {
6568
['yamlContent'],
6669
]);
6770

71+
expect(ignoredFiles.has('lib/path/to/generated/file.g.dart')).toBe(true);
72+
expect(ignoredFiles.has('lib/path/to/normal/file')).toBe(false);
73+
expect(ignoredFiles.has('lib/main.dart')).toBe(false);
74+
expect(ignoredFiles.has('lib/excluded.dart')).toBe(true);
75+
});
76+
test('It should return the ignored files of the closest analysis_options.yaml file', () => {
77+
mockActionOptions.actionOptions = {
78+
workingDirectory: 'working/directory',
79+
} as ActionOptions;
80+
81+
mockPath.resolve.mockReturnValueOnce('working/directory/analysis_options.yaml');
82+
mockFs.existsSync.mockReturnValueOnce(false); // The file is not found.
83+
mockPath.resolve.mockReturnValueOnce('working'); // Returns the parent
84+
mockPath.resolve.mockReturnValueOnce('working/analysis_options.yaml'); // Yaml higher in the file tree.
85+
mockFs.existsSync.mockReturnValueOnce(true); // The file is not found.
86+
mockFs.readFileSync.mockReturnValue('yamlContent');
87+
mockYaml.load.mockReturnValue({
88+
analyzer: {
89+
exclude: [
90+
'**/*.g.dart',
91+
'lib/excluded.dart'
92+
],
93+
}
94+
});
95+
const ignoredFiles = new IgnoredFiles();
96+
97+
expect(mockPath.resolve.mock.calls).toEqual([
98+
['working/directory', 'analysis_options.yaml'],
99+
['working/directory', '..'],
100+
['working', 'analysis_options.yaml'],
101+
]);
102+
expect(mockFs.existsSync.mock.calls).toEqual([
103+
['working/directory/analysis_options.yaml'],
104+
['working/analysis_options.yaml'],
105+
]);
106+
expect(mockFs.readFileSync.mock.calls).toEqual([
107+
['working/analysis_options.yaml', 'utf8'],
108+
]);
109+
expect(mockYaml.load.mock.calls).toEqual([
110+
['yamlContent'],
111+
]);
112+
68113
expect(ignoredFiles.has('lib/path/to/generated/file.g.dart')).toBe(true);
69114
expect(ignoredFiles.has('lib/path/to/normal/file')).toBe(false);
70115
expect(ignoredFiles.has('lib/main.dart')).toBe(false);

src/utils/IgnoredFiles.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import * as fs from 'fs';
22
import * as yaml from 'js-yaml';
3-
import * as minimatch from 'minimatch';
3+
import minimatch from 'minimatch';
44
import * as path from 'path';
55
import { actionOptions } from './ActionOptions';
66

7+
type AnalysisOptions = {
8+
include?: string;
9+
analyzer?: {
10+
exclude?: string[] | string;
11+
}
12+
}
13+
714
/**
815
* The ignore files in the analysis_options.yaml
916
*/
@@ -12,15 +19,61 @@ export class IgnoredFiles {
1219
constructor() {
1320
let patterns: string[];
1421
try {
15-
const yamlFile = yaml.load(fs.readFileSync(path.resolve(actionOptions.workingDirectory, 'analysis_options.yaml'), 'utf8')) as { analyzer?: { exclude?: string[] } };
16-
patterns = yamlFile?.analyzer?.exclude ?? [];
22+
const yamlPath = IgnoredFiles.findClosestYamlFile(actionOptions.workingDirectory);
23+
if (!yamlPath) {
24+
throw new Error(`Could not find any "analysis_options.yaml" in the parent directories of "${actionOptions.workingDirectory}"`);
25+
}
26+
patterns = IgnoredFiles.getIgnoredPatterns(yamlPath);
1727
} catch (error) {
18-
console.log('Could not load analysis_options.yaml:\n', error);
28+
console.error('Could not load analysis_options.yaml:\n', error);
1929
}
2030
patterns ??= [];
2131
this.patterns = patterns.map((pattern) => new minimatch.Minimatch(pattern));
2232
}
2333

34+
/**
35+
*
36+
* @param path
37+
*/
38+
private static findClosestYamlFile(directoryPath: string): string | null {
39+
const yamlPath = path.resolve(directoryPath, 'analysis_options.yaml');
40+
if (fs.existsSync(yamlPath)) {
41+
return yamlPath;
42+
} else {
43+
const parentDirectoryPath = path.resolve(directoryPath, '..');
44+
if (parentDirectoryPath === directoryPath) {
45+
return null;
46+
} else {
47+
return IgnoredFiles.findClosestYamlFile(parentDirectoryPath);
48+
}
49+
}
50+
}
51+
52+
private static getIgnoredPatterns(yamlPath: string): string[] {
53+
const yamlFile = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as AnalysisOptions;
54+
const exclude = yamlFile?.analyzer?.exclude;
55+
let patterns: string[];
56+
if (exclude) {
57+
if (Array.isArray(exclude)) {
58+
patterns = exclude;
59+
} else if (typeof exclude === 'string') {
60+
patterns = [exclude];
61+
}
62+
}
63+
patterns ??= [];
64+
65+
if (yamlFile?.include) {
66+
const newPath = path.resolve(path.dirname(yamlPath), yamlFile.include);
67+
if (fs.existsSync(newPath)) {
68+
return [
69+
...IgnoredFiles.getIgnoredPatterns(newPath),
70+
...patterns,
71+
];
72+
}
73+
}
74+
return patterns;
75+
}
76+
2477
/**
2578
* Whether a file is ignored
2679
*/

0 commit comments

Comments
 (0)