Skip to content

Commit

Permalink
feat(betterer ✨): add global file cache (#712)
Browse files Browse the repository at this point in the history
  • Loading branch information
phenomnomnominal authored Jun 1, 2021
1 parent bc3ca77 commit 2b309df
Show file tree
Hide file tree
Showing 37 changed files with 832 additions and 116 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ website/build
website/.docusaurus
packages/extension/.vscode-test
*.patch
betterer.*.log
betterer.*.log
.betterer.cache
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ website/.docusaurus
*.patch
CHANGELOG.md
betterer.log
.betterer.cache
website/static/.nojekyll
website/static/videos/start.mp4
website/static/videos/watch.mp4
website/static/videos/watch.mp4
4 changes: 4 additions & 0 deletions goldens/api/@betterer/betterer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export declare namespace betterer {
}

export declare type BettererConfig = {
cache: boolean;
cachePath: string;
configPaths: BettererConfigPaths;
cwd: string;
filePaths: BettererConfigPaths;
Expand Down Expand Up @@ -127,6 +129,8 @@ export declare type BettererFileTestResult = {
};

export declare type BettererOptionsBase = Partial<{
cache: boolean;
cachePath: string;
configPaths: BettererOptionsPaths;
cwd: string;
filters: BettererOptionsFilters;
Expand Down
9 changes: 7 additions & 2 deletions goldens/api/@betterer/cli.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export declare type BettererCLIBaseConfig = BettererCLIEnvConfig & {
tsconfig: string;
};

export declare type BettererCLICacheConfig = {
cache: boolean;
cachePath: string;
};

export declare type BettererCLICIConfig = BettererCLIBaseConfig & {
exclude: BettererCLIArguments;
include: BettererCLIArguments;
Expand All @@ -23,14 +28,14 @@ export declare type BettererCLIInitConfig = BettererCLIEnvConfig & {
config: string;
};

export declare type BettererCLIStartConfig = BettererCLIBaseConfig & {
export declare type BettererCLIStartConfig = BettererCLIBaseConfig & BettererCLICacheConfig & {
exclude: BettererCLIArguments;
include: BettererCLIArguments;
strict: boolean;
update: boolean;
};

export declare type BettererCLIWatchConfig = BettererCLIBaseConfig & {
export declare type BettererCLIWatchConfig = BettererCLIBaseConfig & BettererCLICacheConfig & {
ignore: BettererCLIArguments;
};

Expand Down
11 changes: 8 additions & 3 deletions packages/betterer/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as path from 'path';

import { registerExtensions } from '../register';
import { BettererReporterΩ, DEFAULT_REPORTER, loadReporters } from '../reporters';
import { BettererFileResolverΩ } from '../test';
import { BettererFileResolverΩ } from '../runner';
import { isBoolean, isRegExp, isString, isUndefined } from '../utils';
import {
BettererConfig,
Expand Down Expand Up @@ -47,6 +47,8 @@ async function processOptions(options: unknown = {}): Promise<BettererConfig> {

const relativeConfig: BettererConfig = {
// Base:
cache: baseOptions.cache || false,
cachePath: baseOptions.cachePath || './.betterer.cache',
configPaths: baseOptions.configPaths ? toArray<string>(baseOptions.configPaths) : ['./.betterer'],
cwd: baseOptions.cwd || process.cwd(),
filters: toRegExps(toArray<string | RegExp>(baseOptions.filters)),
Expand Down Expand Up @@ -74,11 +76,12 @@ async function processOptions(options: unknown = {}): Promise<BettererConfig> {
const { includes, excludes } = startOptions;

const resolver = new BettererFileResolverΩ(relativeConfig.cwd);
resolver.includeΔ(...toArray<string>(includes));
resolver.excludeΔ(...toRegExps(toArray<string | RegExp>(excludes)));
resolver.include(...toArray<string>(includes));
resolver.exclude(...toRegExps(toArray<string | RegExp>(excludes)));

globalConfig = {
...relativeConfig,
cachePath: path.resolve(relativeConfig.cwd, relativeConfig.cachePath),
filePaths: await resolver.files([]),
configPaths: relativeConfig.configPaths.map((configPath) => path.resolve(relativeConfig.cwd, configPath)),
resultsPath: path.resolve(relativeConfig.cwd, relativeConfig.resultsPath),
Expand All @@ -99,6 +102,8 @@ export function getConfig(): BettererConfig {

function validateConfig(config: BettererConfig): void {
// Base:
validateBool('cache', config);
validateString('cachePath', config);
validateStringArray('configPaths', config);
validateString('cwd', config);
validateStringRegExpArray('filters', config);
Expand Down
4 changes: 4 additions & 0 deletions packages/betterer/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export type BettererConfigReporters = ReadonlyArray<BettererConfigReporter>;

export type BettererConfig = {
// Base:
cache: boolean;
cachePath: string;
configPaths: BettererConfigPaths;
cwd: string;
filePaths: BettererConfigPaths;
Expand All @@ -33,6 +35,8 @@ export type BettererOptionsIncludes = Array<string> | string;
export type BettererOptionsReporters = Array<string | BettererReporter>;

export type BettererOptionsBase = Partial<{
cache: boolean;
cachePath: string;
configPaths: BettererOptionsPaths;
cwd: string;
filters: BettererOptionsFilters;
Expand Down
11 changes: 7 additions & 4 deletions packages/betterer/src/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BettererConfig } from '../config';
import { BettererReporterΩ } from '../reporters';
import { requireUncached } from '../require';
import { BettererResultsΩ, BettererResultΩ } from '../results';
import { BettererFilePaths } from '../runner';
import { BettererFileManager } from '../runner';
import { defer, Defer } from '../utils';
import {
BettererTest,
Expand Down Expand Up @@ -58,7 +58,7 @@ export class BettererContextΩ implements BettererContext {
};
}

public async run(filePaths: BettererFilePaths = []): Promise<BettererSummary> {
public async run(fileManager: BettererFileManager): Promise<BettererSummary> {
if (this._running) {
await this._running;
}
Expand All @@ -71,7 +71,9 @@ export class BettererContextΩ implements BettererContext {
let testNames = Object.keys(this._tests);

// Only run BettererFileTests when a list of filePaths is given:
if (filePaths.length) {
const { filePaths } = fileManager;
const runFileTests = filePaths.length > 0;
if (runFileTests) {
testNames = testNames.filter((name) => isBettererFileTestΔ(this._tests[name]));
}

Expand All @@ -82,7 +84,7 @@ export class BettererContextΩ implements BettererContext {
const isObsolete = obsolete.includes(name);
const baseline = await this._results.getBaseline(name, config);
const expected = await this._results.getExpectedResult(name, config);
return new BettererRunΩ(this._reporter, name, config, expected, baseline, filePaths, isSkipped, isObsolete);
return new BettererRunΩ(this._reporter, name, config, expected, baseline, fileManager, isSkipped, isObsolete);
})
);
const runsLifecycle = defer<BettererSummary>();
Expand All @@ -103,6 +105,7 @@ export class BettererContextΩ implements BettererContext {
runsLifecycle.resolve(summary);
await reportRunsStart;
await this._reporter.runsEnd(summary, filePaths);

return summary;
}

Expand Down
9 changes: 7 additions & 2 deletions packages/betterer/src/context/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BettererResult } from '../results';
import { BettererFilePaths } from '../runner';
import { BettererDiff, BettererTestConfig } from '../test';
import { Defer, defer } from '../utils';
import { BettererFileManager } from '../runner/file-manager';
import { BettererDelta, BettererRun, BettererRunStarted } from './types';

enum BettererRunStatus {
Expand Down Expand Up @@ -38,7 +39,7 @@ export class BettererRunΩ implements BettererRun {
private readonly _test: BettererTestConfig,
private readonly _expected: BettererResult,
private readonly _baseline: BettererResult,
private readonly _filePaths: BettererFilePaths,
private readonly _fileManager: BettererFileManager,
isSkipped: boolean,
isObsolete: boolean
) {
Expand All @@ -65,7 +66,11 @@ export class BettererRunΩ implements BettererRun {
}

public get filePaths(): BettererFilePaths {
return this._filePaths;
return this._fileManager.filePaths;
}

public get fileManager(): BettererFileManager {
return this._fileManager;
}

public get delta(): BettererDelta | null {
Expand Down
12 changes: 8 additions & 4 deletions packages/betterer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ export {
} from './context/public';
export { BettererResult } from './results/public';
export { BettererReporter } from './reporters/public';
export { BettererFilePaths, BettererRunner, BettererRunHandler } from './runner/public';
export {
BettererDeserialise,
BettererDiff,
BettererDiffer,
BettererFileGlobs,
BettererFilePaths,
BettererFilePatterns,
BettererFileResolver,
BettererRunner,
BettererRunHandler
} from './runner/public';
export {
BettererDeserialise,
BettererDiff,
BettererDiffer,
BettererFileTest,
BettererFileTestFunction,
BettererFile,
Expand Down
73 changes: 73 additions & 0 deletions packages/betterer/src/runner/file-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { BettererConfig } from '../config';
import { createHash } from '../hasher';
import { read } from '../reader';
import { write } from '../writer';
import { BettererFileResolverΩ } from './file-resolver';
import { BettererFilePaths } from './types';

type BettererCache = Record<string, string>;

export class BettererFileManager {
private _cache: boolean;
private _cachePath: string;
private _cacheMap: BettererCache = {};

constructor(config: BettererConfig, private readonly _filePaths: BettererFilePaths) {
this._cache = config.cache;
this._cachePath = config.cachePath;
}

public get filePaths(): BettererFilePaths {
return this._filePaths;
}

public async readCache(): Promise<void> {
if (!this._cache) {
return;
}

const cache = await read(this._cachePath);
if (!cache) {
return;
}

this._cacheMap = JSON.parse(cache) as BettererCache;
}

public async writeCache(): Promise<void> {
if (!this._cache) {
return;
}
await write(JSON.stringify(this._cacheMap, null, ' '), this._cachePath);
}

public async getRequested(resolver: BettererFileResolverΩ): Promise<BettererFilePaths> {
return await resolver.files(this.filePaths);
}

public async getActual(resolver: BettererFileResolverΩ): Promise<BettererFilePaths> {
const requestedFilePaths = await this.getRequested(resolver);

if (!this._cache) {
return requestedFilePaths;
}

const actualFilePaths: Array<string> = [];
await Promise.all(
requestedFilePaths.map(async (filePath) => {
const content = await read(filePath);
if (content == null) {
return;
}
const hash = createHash(content);
const relativePath = filePath.replace(resolver.cwd, '');
if (!this._cacheMap[relativePath] || this._cacheMap[relativePath] !== hash) {
actualFilePaths.push(filePath);
}
this._cacheMap[relativePath] = hash;
})
);

return actualFilePaths;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import stack from 'callsite';
import globby from 'globby';
import * as path from 'path';

import { BettererFilePaths } from '../../runner';
import { flatten, normalisedPath } from '../../utils';
import { BettererFileGlobs, BettererFilePatterns } from './types';
import { flatten, normalisedPath } from '../utils';
import { BettererFileGlobs, BettererFilePaths, BettererFilePatterns } from './types';

export class BettererFileResolverΩ {
private _excluded: Array<RegExp> = [];
private _included: Array<string> = [];

constructor(private _cwd: string) {}
constructor(private _cwd: string) {
this._cwd = normalisedPath(this._cwd);
}

public get cwd(): string {
return this._cwd;
Expand All @@ -28,14 +29,12 @@ export class BettererFileResolverΩ {
return normalisedPath(path.resolve(this._cwd, ...pathSegments));
}

/** @internal Definitely not stable! Please don't use! */
public includeΔ(...includePatterns: BettererFileGlobs): this {
public include(...includePatterns: BettererFileGlobs): this {
this._included = [...this._included, ...flatten(includePatterns).map((pattern) => this.resolve(pattern))];
return this;
}

/** @internal Definitely not stable! Please don't use! */
public excludeΔ(...excludePatterns: BettererFilePatterns): this {
public exclude(...excludePatterns: BettererFilePatterns): this {
this._excluded = [...this._excluded, ...flatten(excludePatterns)];
return this;
}
Expand Down Expand Up @@ -104,13 +103,13 @@ export class BettererFileResolver {

/** @internal Definitely not stable! Please don't use! */
public includeΔ(...includePatterns: BettererFileGlobs): this {
this._resolver.includeΔ(...includePatterns);
this._resolver.include(...includePatterns);
return this;
}

/** @internal Definitely not stable! Please don't use! */
public excludeΔ(...excludePatterns: BettererFilePatterns): this {
this._resolver.excludeΔ(...excludePatterns);
this._resolver.exclude(...excludePatterns);
return this;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/betterer/src/runner/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { BettererFileManager } from './file-manager';
export { BettererFileResolver, BettererFileResolverΩ } from './file-resolver';
export { BettererRunnerΩ } from './runner';
export { BettererFilePaths, BettererRunner } from './types';
export { BettererFileGlobs, BettererFilePaths, BettererFilePatterns, BettererRunner } from './types';
export { BettererWatcherΩ } from './watcher';
9 changes: 8 additions & 1 deletion packages/betterer/src/runner/public.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
export { BettererFilePaths, BettererRunner, BettererRunHandler } from './types';
export { BettererFileResolver } from './file-resolver';
export {
BettererFileGlobs,
BettererFilePaths,
BettererFilePatterns,
BettererRunner,
BettererRunHandler
} from './types';
7 changes: 6 additions & 1 deletion packages/betterer/src/runner/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BettererConfig } from '../config';
import { BettererContextΩ, BettererContextStarted, BettererSummary } from '../context';
import { BettererReporterΩ } from '../reporters';
import { normalisedPath } from '../utils';
import { BettererFileManager } from './file-manager';
import { BettererFilePaths, BettererRunHandler, BettererRunner, BettererRunnerJobs } from './types';

const DEBOUNCE_TIME = 200;
Expand Down Expand Up @@ -74,7 +75,11 @@ export class BettererRunnerΩ implements BettererRunner {

private async _runTests(filePaths: BettererFilePaths): Promise<BettererSummary> {
try {
return this._context.run(filePaths);
const fileManager = new BettererFileManager(this._context.config, filePaths);
await fileManager.readCache();
const summary = await this._context.run(fileManager);
await fileManager.writeCache();
return summary;
} catch (error) {
await this._started.error(error);
throw error;
Expand Down
3 changes: 3 additions & 0 deletions packages/betterer/src/runner/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { BettererSummary } from '../context';

export type BettererFileGlobs = ReadonlyArray<string | ReadonlyArray<string>>;
export type BettererFilePaths = ReadonlyArray<string>;
export type BettererFilePatterns = ReadonlyArray<RegExp | ReadonlyArray<RegExp>>;

export type BettererRunHandler = (summary: BettererSummary) => void;

export type BettererRunner = {
Expand Down
Loading

0 comments on commit 2b309df

Please sign in to comment.