Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE-316: fix support for mixed base directories #320

Merged
merged 2 commits into from
Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/managers/tasks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,20 @@ describe('Managers → Task', () => {
assert.deepStrictEqual(actual, expected);
});

it('should return two tasks', () => {
it('should return two tasks when one of patterns contains reference to the parent directory', () => {
const expected = [
tests.task.builder().base('..').positive('../*.md').build(),
tests.task.builder().base('.').positive('*').positive('a/*').negative('*.md').build()
];

const actual = manager.convertPatternsToTasks(['*', 'a/*', '../*.md'], ['*.md'], /* dynamic */ true);

console.dir(actual, { colors: true });

assert.deepStrictEqual(actual, expected);
});

it('should return two tasks when all patterns refers to the different base directories', () => {
const expected = [
tests.task.builder().base('a').positive('a/*').negative('b/*.md').build(),
tests.task.builder().base('b').positive('b/*').negative('b/*.md').build()
Expand Down
30 changes: 23 additions & 7 deletions src/managers/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,34 @@ export function generate(patterns: Pattern[], settings: Settings): Task[] {
return staticTasks.concat(dynamicTasks);
}

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

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

return [task];
const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);

tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, [], dynamic));

/*
* For the sake of reducing future accesses to the file system, we merge all tasks within the current directory
* into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest.
*/
if ('.' in insideCurrentDirectoryGroup) {
tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic));
} else {
tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
}

return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic);
return tasks;
}

export function getPositivePatterns(patterns: Pattern[]): Pattern[] {
Expand Down
46 changes: 46 additions & 0 deletions src/utils/pattern.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,52 @@ describe('Utils → Pattern', () => {
});
});

describe('.getPatternsInsideCurrentDirectory', () => {
it('should return patterns', () => {
const expected: Pattern[] = ['.', './*', '*', 'a/*'];

const actual = util.getPatternsInsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);

assert.deepStrictEqual(actual, expected);
});
});

describe('.getPatternsOutsideCurrentDirectory', () => {
it('should return patterns', () => {
const expected: Pattern[] = ['..', '../*', './..', './../*'];

const actual = util.getPatternsOutsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);

assert.deepStrictEqual(actual, expected);
});
});

describe('.isPatternRelatedToParentDirectory', () => {
it('should be `false` when the pattern refers to the current directory', () => {
const actual = util.isPatternRelatedToParentDirectory('.');

assert.ok(!actual);
});

it('should be `true` when the pattern equals to `..`', () => {
const actual = util.isPatternRelatedToParentDirectory('..');

assert.ok(actual);
});

it('should be `true` when the pattern starts with `..` segment', () => {
const actual = util.isPatternRelatedToParentDirectory('../*');

assert.ok(actual);
});

it('should be `true` when the pattern starts with `./..` segment', () => {
const actual = util.isPatternRelatedToParentDirectory('./../*');

assert.ok(actual);
});
});

describe('.getBaseDirectory', () => {
it('should returns base directory', () => {
const expected = 'root';
Expand Down
26 changes: 26 additions & 0 deletions src/utils/pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,32 @@ export function getPositivePatterns(patterns: Pattern[]): Pattern[] {
return patterns.filter(isPositivePattern);
}

/**
* Returns patterns that can be applied inside the current directory.
*
* @example
* // ['./*', '*', 'a/*']
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
*/
export function getPatternsInsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern));
}

/**
* Returns patterns to be expanded relative to (outside) the current directory.
*
* @example
* // ['../*', './../*']
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
*/
export function getPatternsOutsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
return patterns.filter(isPatternRelatedToParentDirectory);
}

export function isPatternRelatedToParentDirectory(pattern: Pattern): boolean {
return pattern.startsWith('..') || pattern.startsWith('./..');
}

export function getBaseDirectory(pattern: Pattern): string {
return globParent(pattern, { flipBackslashes: false });
}
Expand Down