diff --git a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts index 5450d0228b..8caa853c6b 100644 --- a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts +++ b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts @@ -38,6 +38,11 @@ type Options = { parser?: TodoParser; }; +type CacheItem = { + etag: string; + result: ReadTodosResult; +}; + const defaultTodoParser: TodoParser = ({ content, path }) => { try { const comments = parse(content, { @@ -60,6 +65,8 @@ export class TodoScmReader implements TodoReader { private readonly parser: TodoParser; private readonly integrations: ScmIntegrations; + private readonly cache = new Map(); + static fromConfig(config: Config, options: Options) { return new TodoScmReader(options, ScmIntegrations.fromConfig(config)); } @@ -72,7 +79,25 @@ export class TodoScmReader implements TodoReader { } async readTodos({ url }: ReadTodosOptions): Promise { + const cacheItem = this.cache.get(url); + try { + const newCacheItem = await this.doReadTodos({ url }, cacheItem?.etag); + this.cache.set(url, newCacheItem); + return newCacheItem.result; + } catch (error) { + if (cacheItem && error.name === 'NotModifiedError') { + return cacheItem.result; + } + throw error; + } + } + + private async doReadTodos( + { url }: ReadTodosOptions, + etag?: string, + ): Promise { const tree = await this.reader.readTree(url, { + etag, filter(path) { return !path.startsWith('.yarn'); }, @@ -113,6 +138,6 @@ export class TodoScmReader implements TodoReader { } } - return { items: todos }; + return { result: { items: todos }, etag: tree.etag }; } }