diff --git a/.changeset/new-beans-warn.md b/.changeset/new-beans-warn.md new file mode 100644 index 0000000000..bd4703f8a6 --- /dev/null +++ b/.changeset/new-beans-warn.md @@ -0,0 +1,19 @@ +--- +"@lynx-js/react": patch +--- + +Use error cause to simplify the error msg of lazy bundle loading. User can catch the error cause to get the original result: + +```ts +const LazyComponent = lazy(async () => { + try { + const mod = await import('./lazy-bundle'); + return mod.default; + } catch (error) { + console.error(`Lazy Bundle load failed message: ${error.message}`); + // User can catch the error cause to get the original result + console.error(`Lazy Bundle load failed result: ${error.cause}`); + throw error; + } +}); +``` diff --git a/packages/react/runtime/__test__/lynx/lazy-bundle.test.js b/packages/react/runtime/__test__/lynx/lazy-bundle.test.js index 49e2530da7..5764ba6c35 100644 --- a/packages/react/runtime/__test__/lynx/lazy-bundle.test.js +++ b/packages/react/runtime/__test__/lynx/lazy-bundle.test.js @@ -240,7 +240,7 @@ describe('loadLazyBundle', () => { test('blocking QueryComponent with error', async () => { QueryComponent.mockImplementation((source, callback) => { - callback({ code: 1, detail: { errMsg: 'error', source } }); + callback({ code: 1, detail: { errMsg: 'error', schema: source } }); }); const { loadLazyBundle } = await import('../../src/lynx/lazy-bundle'); @@ -257,14 +257,14 @@ describe('loadLazyBundle', () => { let catchCalled = false; const promise3 = promise.catch((err) => { expect(err).toMatchInlineSnapshot( - `[Error: Lazy bundle load failed: {"code":1,"detail":{"errMsg":"error","source":"foo"}}]`, + `[Error: Lazy bundle load failed, schema: foo]`, ); catchCalled = true; }); expect(catchCalled).toBe(false); // Should be non-blocking await expect(promise2).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Lazy bundle load failed: {"code":1,"detail":{"errMsg":"error","source":"foo"}}]`, + `[Error: Lazy bundle load failed, schema: foo]`, ); await promise3; @@ -447,7 +447,7 @@ describe('loadLazyBundle', () => { test('non-blocking QueryComponent with rejections', async () => { QueryComponent.mockImplementation((source, callback) => { Promise.resolve().then(() => { - callback({ code: 1, detail: { errMsg: 'error', source } }); + callback({ code: 1, detail: { errMsg: 'error', schema: source } }); }); }); @@ -462,7 +462,7 @@ describe('loadLazyBundle', () => { expect.fail('promise should not resolve'); }, (err) => { expect(err).toMatchInlineSnapshot( - `[Error: Lazy bundle load failed: {"code":1,"detail":{"errMsg":"error","source":"foo"}}]`, + `[Error: Lazy bundle load failed, schema: foo]`, ); thenCalled = true; return 'bar'; @@ -470,7 +470,7 @@ describe('loadLazyBundle', () => { expect(thenCalled).toBe(false); await expect(promise).rejects.toMatchInlineSnapshot( - `[Error: Lazy bundle load failed: {"code":1,"detail":{"errMsg":"error","source":"foo"}}]`, + `[Error: Lazy bundle load failed, schema: foo]`, ); expect(thenCalled).toBe(true); @@ -485,6 +485,52 @@ describe('loadLazyBundle', () => { expect(thenCalled).toBe(true); }); + test('catch and process error of loadLazyBundle', async () => { + QueryComponent.mockImplementation((source, callback) => { + Promise.resolve().then(() => { + callback({ code: 1, detail: { errMsg: 'error', schema: source } }); + }); + }); + + const { loadLazyBundle } = await import('../../src/lynx/lazy-bundle'); + + const promise = loadLazyBundle('foo'); + + expect(QueryComponent).toBeCalledWith('foo', expect.any(Function)); + + let thenCalled = false; + const promise2 = promise.then(() => { + expect.fail('promise should not resolve'); + }, (err) => { + const cause = err.cause; + expect(cause).toMatchInlineSnapshot( + `"{"code":1,"detail":{"errMsg":"error","schema":"foo"}}"`, + ); + thenCalled = true; + return `Lazy Bundle load failed result: ${cause}`; + }); + expect(thenCalled).toBe(false); + + await expect(promise).rejects.toMatchInlineSnapshot( + `[Error: Lazy bundle load failed, schema: foo]`, + ); + expect(thenCalled).toBe(true); + + thenCalled = false; + promise2.then((data) => { + expect(data).toMatchInlineSnapshot( + `"Lazy Bundle load failed result: {"code":1,"detail":{"errMsg":"error","schema":"foo"}}"`, + ); + thenCalled = true; + }); + expect(thenCalled).toBe(false); + + await expect(promise2).resolves.toMatchInlineSnapshot( + `"Lazy Bundle load failed result: {"code":1,"detail":{"errMsg":"error","schema":"foo"}}"`, + ); + expect(thenCalled).toBe(true); + }); + test('lynx.getNativeLynx().QueryComponent', async () => { QueryComponent.mockImplementation((source, callback) => { callback({ code: 0, detail: { schema: source } }); diff --git a/packages/react/runtime/src/lynx/lazy-bundle.ts b/packages/react/runtime/src/lynx/lazy-bundle.ts index a5b437ce41..f4494e8775 100644 --- a/packages/react/runtime/src/lynx/lazy-bundle.ts +++ b/packages/react/runtime/src/lynx/lazy-bundle.ts @@ -103,7 +103,11 @@ export const loadLazyBundle: < return; } } - resolver.reject(new Error('Lazy bundle load failed: ' + JSON.stringify(result))); + const e = new Error('Lazy bundle load failed, schema: ' + result.detail.schema); + // ES5 does not support new Error('message', { cause: 'detail' }) + // So we set cause using `.cause` assignment + e.cause = JSON.stringify(result); + resolver.reject(e); }; if (typeof lynx.QueryComponent === 'function') { lynx.QueryComponent(source, callback);