diff --git a/.changeset/graph-rebuild-after-error.md b/.changeset/graph-rebuild-after-error.md new file mode 100644 index 000000000..3c386807e --- /dev/null +++ b/.changeset/graph-rebuild-after-error.md @@ -0,0 +1,5 @@ +--- +'@shopify/theme-language-server-common': patch +--- + +Rebuild a theme graph that failed to build when a file changes. Before, the failed graph stayed until the language server restarted. diff --git a/packages/theme-language-server-common/src/server/ThemeGraphManager.spec.ts b/packages/theme-language-server-common/src/server/ThemeGraphManager.spec.ts new file mode 100644 index 000000000..0b94d41e1 --- /dev/null +++ b/packages/theme-language-server-common/src/server/ThemeGraphManager.spec.ts @@ -0,0 +1,45 @@ +import { AbstractFileSystem, path } from '@shopify/theme-check-common'; +import { MockFileSystem } from '@shopify/theme-check-common/src/test'; +import { describe, expect, it, vi } from 'vitest'; +import { DocumentManager } from '../documents'; +import { mockConnection } from '../test/MockConnection'; +import { ThemeGraphManager } from './ThemeGraphManager'; + +describe('Module: ThemeGraphManager', () => { + it('rebuilds a graph that failed to build when a file changes', async () => { + const rootUri = path.normalize('file:/'); + const mockFs = new MockFileSystem( + { + 'assets/theme.js': '', + 'sections/main.liquid': "{% render 'card' %}", + 'snippets/card.liquid': '', + }, + rootUri, + ); + let broken = true; + const fs: AbstractFileSystem = { + stat: (uri) => mockFs.stat(uri), + readFile: (uri) => mockFs.readFile(uri), + readDirectory: (uri) => + broken ? Promise.reject(new Error('EIO')) : mockFs.readDirectory(uri), + }; + const connection = mockConnection(rootUri); + const manager = new ThemeGraphManager( + connection, + new DocumentManager(fs), + fs, + async () => rootUri, + ); + const card = path.join(rootUri, 'snippets', 'card.liquid'); + + await expect(manager.getReferences(card)).rejects.toThrow('EIO'); + + broken = false; + vi.useFakeTimers(); + manager.change(card); + await vi.runAllTimersAsync(); + vi.useRealTimers(); + + expect(await manager.getReferences(card)).toHaveLength(1); + }); +}); diff --git a/packages/theme-language-server-common/src/server/ThemeGraphManager.ts b/packages/theme-language-server-common/src/server/ThemeGraphManager.ts index a83bcbced..63eb6881d 100644 --- a/packages/theme-language-server-common/src/server/ThemeGraphManager.ts +++ b/packages/theme-language-server-common/src/server/ThemeGraphManager.ts @@ -208,8 +208,10 @@ export class ThemeGraphManager { const rootUri = await this.findThemeRootURI(anyUri); if (!rootUri) return; - const graph = await this.graphs.get(rootUri); + const graph = this.graphs.get(rootUri); if (!graph) return; + // Let a build in progress finish first. A graph that failed to build is rebuilt too. + await graph.catch(() => {}); this.graphs.delete(rootUri); await this.getThemeGraphForURI(rootUri);