Skip to content

Commit

Permalink
feat: test relative paths (fixes #34) (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus authored Mar 27, 2023
1 parent dfbb0f5 commit 0d27f7a
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 38 deletions.
4 changes: 2 additions & 2 deletions src/backends/ChokidarWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ export class ChokidarWatcher extends FileWatchingBackend {
});

this.chokidar.on('ready', () => {
this.emit('ready');
this.emitReady();

this.chokidar.on('all', (event, filename) => {
if (event === 'addDir') {
return;
}

this.emit('change', { filename });
this.emitChange({ filename });
});
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/backends/FSWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class FSWatcher extends FileWatchingBackend {
recursive: true,
},
(eventType, filename) => {
this.emit('change', { filename: path.resolve(target, filename) });
this.emitChange({ filename: path.resolve(target, filename) });
},
);
};
Expand All @@ -93,7 +93,7 @@ export class FSWatcher extends FileWatchingBackend {
(eventType, filename) => {
const absolutePath = path.resolve(symlink.realpath, filename);

this.emit('change', {
this.emitChange({
filename: path.join(
symlink.symlink,
path.relative(symlink.realpath, absolutePath),
Expand All @@ -104,7 +104,7 @@ export class FSWatcher extends FileWatchingBackend {
);
}

this.emit('ready');
this.emitReady();
});
}

Expand Down
19 changes: 17 additions & 2 deletions src/backends/FileWatchingBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

import { type FileChangeEvent } from '../types';
import { EventEmitter } from 'node:events';
import path from 'node:path';

interface BackendEventEmitter {
on(event: 'ready', listener: () => void): this;
on(event: 'change', listener: ({ filename }: FileChangeEvent) => void): this;
on(name: 'ready', listener: () => void): this;
on(name: 'change', listener: ({ filename }: FileChangeEvent) => void): this;
}

export abstract class FileWatchingBackend
Expand All @@ -18,4 +19,18 @@ export abstract class FileWatchingBackend
}

public abstract close(): Promise<void>;

protected emitReady(): void {
this.emit('ready');
}

protected emitChange(event: FileChangeEvent): void {
if (!path.isAbsolute(event.filename)) {
throw new Error('Watchers must emit absolute paths');
}

this.emit('change', {
filename: event.filename,
});
}
}
58 changes: 28 additions & 30 deletions src/testExpression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,107 +3,105 @@ import { expect, it } from 'vitest';

it('[allof] evaluates as true if all of the grouped expressions also evaluated as true (true)', () => {
expect(
testExpression(['allof', ['match', 'bar', 'basename']], '/foo/bar'),
testExpression(['allof', ['match', 'bar', 'basename']], 'foo/bar'),
).toBe(true);
});

it('[allof] evaluates as true if all of the grouped expressions also evaluated as true (false, true)', () => {
expect(
testExpression(
['allof', ['match', 'foo', 'basename'], ['match', 'bar', 'basename']],
'/foo/bar',
'foo/bar',
),
).toBe(false);
});

it('[allof] evaluates as true if all of the grouped expressions also evaluated as true (false)', () => {
expect(
testExpression(['allof', ['match', 'foo', 'basename']], '/foo/bar'),
testExpression(['allof', ['match', 'foo', 'basename']], 'foo/bar'),
).toBe(false);
});

it('[anyof] evaluates as true if any of the grouped expressions also evaluated as true (true)', () => {
expect(
testExpression(['anyof', ['match', 'bar', 'basename']], '/foo/bar'),
testExpression(['anyof', ['match', 'bar', 'basename']], 'foo/bar'),
).toBe(true);
});

it('[anyof] evaluates as true if any of the grouped expressions also evaluated as true (false, true)', () => {
expect(
testExpression(
['anyof', ['match', 'foo', 'basename'], ['match', 'bar', 'basename']],
'/foo/bar',
'foo/bar',
),
).toBe(true);
});

it('[anyof] evaluates as true if any of the grouped expressions also evaluated as true (false)', () => {
expect(
testExpression(['anyof', ['match', 'foo', 'basename']], '/foo/bar'),
testExpression(['anyof', ['match', 'foo', 'basename']], 'foo/bar'),
).toBe(false);
});

it('[dirname] dot directory in subject does not break the pattern', () => {
expect(
testExpression(['dirname', 'node_modules'], '/node_modules/.dist/foo.js'),
testExpression(['dirname', 'node_modules'], 'node_modules/.dist/foo.js'),
).toBe(true);
});

it('[dirname] evaluates as true if a given file has a matching parent directory (foo)', () => {
expect(testExpression(['dirname', 'foo'], '/foo/bar')).toBe(true);
expect(testExpression(['dirname', 'bar'], '/foo/bar/baz')).toBe(true);
expect(testExpression(['dirname', 'bar/baz'], '/foo/bar/baz/qux')).toBe(true);
expect(testExpression(['dirname', '/foo/bar'], '/foo/bar/baz/qux')).toBe(
true,
);
expect(testExpression(['dirname', 'foo'], 'foo/bar')).toBe(true);
expect(testExpression(['dirname', 'bar'], 'foo/bar/baz')).toBe(true);
expect(testExpression(['dirname', 'bar/baz'], 'foo/bar/baz/qux')).toBe(true);
expect(testExpression(['dirname', 'foo/bar'], 'foo/bar/baz/qux')).toBe(true);
});

it('[dirname] evaluates as false if a given file does not have a matching parent directory (bar)', () => {
expect(testExpression(['dirname', 'bar'], '/foo/bar')).toBe(false);
expect(testExpression(['dirname', '/bar'], '/foo/bar/baz')).toBe(false);
expect(testExpression(['dirname', 'foo'], '/.foo/bar')).toBe(false);
expect(testExpression(['dirname', 'bar'], 'foo/bar')).toBe(false);
expect(testExpression(['dirname', '/bar'], 'foo/bar/baz')).toBe(false);
expect(testExpression(['dirname', 'foo'], '.foo/bar')).toBe(false);
});

it('[idirname] evaluates as true if a given file has a matching parent directory (foo)', () => {
expect(testExpression(['idirname', 'FOO'], '/foo/bar')).toBe(true);
expect(testExpression(['idirname', 'FOO'], 'foo/bar')).toBe(true);
});

it('[idirname] evaluates as false if a given file does not have a matching parent directory (bar)', () => {
expect(testExpression(['idirname', 'BAR'], '/foo/bar')).toBe(false);
expect(testExpression(['idirname', 'BAR'], 'foo/bar')).toBe(false);
});

it('[match] matches basename (bar)', () => {
expect(testExpression(['match', 'bar', 'basename'], '/foo/bar')).toBe(true);
expect(testExpression(['match', 'bar', 'basename'], 'foo/bar')).toBe(true);
});

it('[match] matches basename (b*r)', () => {
expect(testExpression(['match', 'b*r', 'basename'], '/foo/bar')).toBe(true);
expect(testExpression(['match', 'b*r', 'basename'], 'foo/bar')).toBe(true);
});

it('[match] does not match basename (bar)', () => {
expect(testExpression(['match', 'foo', 'basename'], '/foo/bar')).toBe(false);
expect(testExpression(['match', 'foo', 'basename'], 'foo/bar')).toBe(false);
});

it('[match] matches basename (BAR) (case insensitive)', () => {
expect(testExpression(['imatch', 'bar', 'basename'], '/foo/bar')).toBe(true);
expect(testExpression(['imatch', 'bar', 'basename'], 'foo/bar')).toBe(true);
});

it('[match] matches basename (B*R) (case insensitive)', () => {
expect(testExpression(['imatch', 'b*r', 'basename'], '/foo/bar')).toBe(true);
expect(testExpression(['imatch', 'b*r', 'basename'], 'foo/bar')).toBe(true);
});

it('[match] does not match basename (BAR) (case insensitive)', () => {
expect(testExpression(['imatch', 'foo', 'basename'], '/foo/bar')).toBe(false);
expect(testExpression(['imatch', 'foo', 'basename'], 'foo/bar')).toBe(false);
});

it('[not] evaluates as true if the sub-expression evaluated as false, i.e. inverts the sub-expression (true -> false)', () => {
expect(
testExpression(['not', ['match', 'bar', 'basename']], '/foo/bar'),
).toBe(false);
expect(testExpression(['not', ['match', 'bar', 'basename']], 'foo/bar')).toBe(
false,
);
});

it('[not] evaluates as true if the sub-expression evaluated as false, i.e. inverts the sub-expression (false -> true)', () => {
expect(
testExpression(['not', ['match', 'foo', 'basename']], '/foo/bar'),
).toBe(true);
expect(testExpression(['not', ['match', 'foo', 'basename']], 'foo/bar')).toBe(
true,
);
});
4 changes: 4 additions & 0 deletions src/testExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import micromatch from 'micromatch';
import path from 'node:path';

export const testExpression = (expression: Expression, fileName: string) => {
if (path.isAbsolute(fileName)) {
throw new Error('File name must be relative');
}

const name = expression[0];

if (name === 'allof') {
Expand Down
3 changes: 2 additions & 1 deletion src/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type TurbowatchConfigurationInput,
type TurbowatchController,
} from './types';
import path from 'node:path';
import { serializeError } from 'serialize-error';
import { debounce } from 'throttle-debounce';

Expand Down Expand Up @@ -146,7 +147,7 @@ export const watch = (
(fileChangeEvent) => {
return testExpression(
subscription.expression,
fileChangeEvent.filename,
path.relative(project, fileChangeEvent.filename),
);
},
);
Expand Down

0 comments on commit 0d27f7a

Please sign in to comment.