Skip to content

Commit c9f7a81

Browse files
authored
Merge pull request #320 from mrmlnc/ISSUE-316_fix_mixed_base_directories
ISSUE-316: fix support for mixed base directories
2 parents e4a3e7f + 4c36f6d commit c9f7a81

File tree

4 files changed

+109
-8
lines changed

4 files changed

+109
-8
lines changed

src/managers/tasks.spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,20 @@ describe('Managers → Task', () => {
5757
assert.deepStrictEqual(actual, expected);
5858
});
5959

60-
it('should return two tasks', () => {
60+
it('should return two tasks when one of patterns contains reference to the parent directory', () => {
61+
const expected = [
62+
tests.task.builder().base('..').positive('../*.md').build(),
63+
tests.task.builder().base('.').positive('*').positive('a/*').negative('*.md').build()
64+
];
65+
66+
const actual = manager.convertPatternsToTasks(['*', 'a/*', '../*.md'], ['*.md'], /* dynamic */ true);
67+
68+
console.dir(actual, { colors: true });
69+
70+
assert.deepStrictEqual(actual, expected);
71+
});
72+
73+
it('should return two tasks when all patterns refers to the different base directories', () => {
6174
const expected = [
6275
tests.task.builder().base('a').positive('a/*').negative('b/*.md').build(),
6376
tests.task.builder().base('b').positive('b/*').negative('b/*.md').build()

src/managers/tasks.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,34 @@ export function generate(patterns: Pattern[], settings: Settings): Task[] {
2323
return staticTasks.concat(dynamicTasks);
2424
}
2525

26+
/**
27+
* Returns tasks grouped by basic pattern directories.
28+
*
29+
* Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately.
30+
* This is necessary because directory traversal starts at the base directory and goes deeper.
31+
*/
2632
export function convertPatternsToTasks(positive: Pattern[], negative: Pattern[], dynamic: boolean): Task[] {
27-
const positivePatternsGroup = groupPatternsByBaseDirectory(positive);
33+
const tasks: Task[] = [];
2834

29-
// When we have a global group – there is no reason to divide the patterns into independent tasks.
30-
// In this case, the global task covers the rest.
31-
if ('.' in positivePatternsGroup) {
32-
const task = convertPatternGroupToTask('.', positive, negative, dynamic);
35+
const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive);
36+
const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive);
3337

34-
return [task];
38+
const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
39+
const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);
40+
41+
tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, [], dynamic));
42+
43+
/*
44+
* For the sake of reducing future accesses to the file system, we merge all tasks within the current directory
45+
* into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest.
46+
*/
47+
if ('.' in insideCurrentDirectoryGroup) {
48+
tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic));
49+
} else {
50+
tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
3551
}
3652

37-
return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic);
53+
return tasks;
3854
}
3955

4056
export function getPositivePatterns(patterns: Pattern[]): Pattern[] {

src/utils/pattern.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,52 @@ describe('Utils → Pattern', () => {
257257
});
258258
});
259259

260+
describe('.getPatternsInsideCurrentDirectory', () => {
261+
it('should return patterns', () => {
262+
const expected: Pattern[] = ['.', './*', '*', 'a/*'];
263+
264+
const actual = util.getPatternsInsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);
265+
266+
assert.deepStrictEqual(actual, expected);
267+
});
268+
});
269+
270+
describe('.getPatternsOutsideCurrentDirectory', () => {
271+
it('should return patterns', () => {
272+
const expected: Pattern[] = ['..', '../*', './..', './../*'];
273+
274+
const actual = util.getPatternsOutsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);
275+
276+
assert.deepStrictEqual(actual, expected);
277+
});
278+
});
279+
280+
describe('.isPatternRelatedToParentDirectory', () => {
281+
it('should be `false` when the pattern refers to the current directory', () => {
282+
const actual = util.isPatternRelatedToParentDirectory('.');
283+
284+
assert.ok(!actual);
285+
});
286+
287+
it('should be `true` when the pattern equals to `..`', () => {
288+
const actual = util.isPatternRelatedToParentDirectory('..');
289+
290+
assert.ok(actual);
291+
});
292+
293+
it('should be `true` when the pattern starts with `..` segment', () => {
294+
const actual = util.isPatternRelatedToParentDirectory('../*');
295+
296+
assert.ok(actual);
297+
});
298+
299+
it('should be `true` when the pattern starts with `./..` segment', () => {
300+
const actual = util.isPatternRelatedToParentDirectory('./../*');
301+
302+
assert.ok(actual);
303+
});
304+
});
305+
260306
describe('.getBaseDirectory', () => {
261307
it('should returns base directory', () => {
262308
const expected = 'root';

src/utils/pattern.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,32 @@ export function getPositivePatterns(patterns: Pattern[]): Pattern[] {
8181
return patterns.filter(isPositivePattern);
8282
}
8383

84+
/**
85+
* Returns patterns that can be applied inside the current directory.
86+
*
87+
* @example
88+
* // ['./*', '*', 'a/*']
89+
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
90+
*/
91+
export function getPatternsInsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
92+
return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern));
93+
}
94+
95+
/**
96+
* Returns patterns to be expanded relative to (outside) the current directory.
97+
*
98+
* @example
99+
* // ['../*', './../*']
100+
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
101+
*/
102+
export function getPatternsOutsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
103+
return patterns.filter(isPatternRelatedToParentDirectory);
104+
}
105+
106+
export function isPatternRelatedToParentDirectory(pattern: Pattern): boolean {
107+
return pattern.startsWith('..') || pattern.startsWith('./..');
108+
}
109+
84110
export function getBaseDirectory(pattern: Pattern): string {
85111
return globParent(pattern, { flipBackslashes: false });
86112
}

0 commit comments

Comments
 (0)