Repository: compiler-explorer/compiler-explorer
Pull request #7734 (2025-05-27 17:22:43)

Refactor startup to catch exceptions where they're thrown. 
Fixes issues with missing components (e.g. from beta, 'explain')

The issue was that the exceptions are thrown in `initLayout`, not in the constructor.



Patch:
diff --git a/static/main.ts b/static/main.ts
index b593620ac..f9f7891f5 100644
--- a/static/main.ts
+++ b/static/main.ts
@@ -613,23 +613,30 @@ function start() {
 
     let layout: GoldenLayout;
     let hub: Hub;
+    let themer: Themer;
+    let settings: SiteSettings;
+
+    function initializeLayout(config: any, root: JQuery<HTMLElement>): [GoldenLayout, Hub, Themer, SiteSettings] {
+        const layout = new GoldenLayout(config, root);
+        const hub = new Hub(layout, subLangId, defaultLangId);
+        const [themer, settings] = setupSettings(hub);
+        hub.initLayout();
+        return [layout, hub, themer, settings];
+    }
+
     try {
-        layout = new GoldenLayout(config, root);
-        hub = new Hub(layout, subLangId, defaultLangId);
+        [layout, hub, themer, settings] = initializeLayout(config, root);
     } catch (e) {
         SentryCapture(e, 'goldenlayout/hub setup');
+        console.log('Exception processing state, resetting layout to default', e);
 
         if (document.URL.includes('/z/')) {
             document.location = document.URL.replace('/z/', '/resetlayout/');
         }
 
-        layout = new GoldenLayout(defaultConfig, root);
-        hub = new Hub(layout, subLangId, defaultLangId);
+        [layout, hub, themer, settings] = initializeLayout(defaultConfig, root);
     }
 
-    const [themer, settings] = setupSettings(hub);
-    hub.initLayout();
-
     setSentryLayout(layout);
 
     if (hub.hasTree()) {


Test patch:
diff --git a/test/filter-tests.ts b/test/filter-tests.ts
index 4b3706392..6ecfa1c30 100644
--- a/test/filter-tests.ts
+++ b/test/filter-tests.ts
@@ -61,13 +61,18 @@ const stringifyKeysInOrder = (data: any): string => {
 
 function testFilter(filename: string, suffix: string, filters: ParseFiltersAndOutputOptions) {
     const testName = path.basename(filename + suffix);
-    it(testName, async () => {
-        const result = processAsm(filename, filters);
-        delete result.parsingTime;
-        delete result.filteredCount;
-        // TODO normalize line endings?
-        await expect(stringifyKeysInOrder(result)).toMatchFileSnapshot(path.join(casesRoot, testName + '.json'));
-    }, 10000); // Bump the timeout a bit so that we don't fail for slow cases
+    it(
+        testName,
+        // Bump the timeout a bit so that we don't fail for slow cases
+        {timeout: 10000},
+        async () => {
+            const result = processAsm(filename, filters);
+            delete result.parsingTime;
+            delete result.filteredCount;
+            // TODO normalize line endings?
+            await expect(stringifyKeysInOrder(result)).toMatchFileSnapshot(path.join(casesRoot, testName + '.json'));
+        },
+    );
 }
 
 describe('Filter test cases', () => {
diff --git a/test/packager-tests.ts b/test/packager-tests.ts
index 8e91a2cbc..b1a5d09ff 100644
--- a/test/packager-tests.ts
+++ b/test/packager-tests.ts
@@ -36,32 +36,40 @@ async function writeTestFile(filepath) {
 }
 
 describe('Packager', () => {
-    it('should be able to package 1 file', async () => {
-        const pack = new Packager();
+    it(
+        'should be able to package 1 file',
+        async () => {
+            const pack = new Packager();
 
-        const dirPath = newTempDir();
-        await writeTestFile(path.join(dirPath, 'hello.txt'));
+            const dirPath = newTempDir();
+            await writeTestFile(path.join(dirPath, 'hello.txt'));
 
-        const targzPath = path.join(dirPath, 'package.tgz');
-        await pack.package(dirPath, targzPath);
+            const targzPath = path.join(dirPath, 'package.tgz');
+            await pack.package(dirPath, targzPath);
 
-        await expect(fs.stat(targzPath)).resolves.toBeDefined();
-    }, 5000);
+            await expect(fs.stat(targzPath)).resolves.toBeDefined();
+        },
+        {timeout: 5000},
+    );
 
-    it('should be able to unpack', async () => {
-        const pack = new Packager();
+    it(
+        'should be able to unpack',
+        async () => {
+            const pack = new Packager();
 
-        const dirPath = newTempDir();
-        await writeTestFile(path.join(dirPath, 'hello.txt'));
+            const dirPath = newTempDir();
+            await writeTestFile(path.join(dirPath, 'hello.txt'));
 
-        const targzPath = path.join(dirPath, 'package.tgz');
-        await pack.package(dirPath, targzPath);
+            const targzPath = path.join(dirPath, 'package.tgz');
+            await pack.package(dirPath, targzPath);
 
-        const unpackPath = newTempDir();
-        const pack2 = new Packager();
-        await pack2.unpack(targzPath, unpackPath);
+            const unpackPath = newTempDir();
+            const pack2 = new Packager();
+            await pack2.unpack(targzPath, unpackPath);
 
-        const unpackedFilepath = path.join(unpackPath, 'hello.txt');
-        await expect(fs.stat(unpackedFilepath)).resolves.toBeDefined;
-    }, 5000);
+            const unpackedFilepath = path.join(unpackPath, 'hello.txt');
+            await expect(fs.stat(unpackedFilepath)).resolves.toBeDefined;
+        },
+        {timeout: 5000},
+    );
 });