Skip to content

Commit

Permalink
feat: exit render when running into fatal-error
Browse files Browse the repository at this point in the history
  • Loading branch information
liximomo committed Mar 19, 2024
1 parent 5f2e284 commit f645b2e
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 13 deletions.
1 change: 1 addition & 0 deletions packages/platform-shared/src/shared/applicationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface IError {
message?: string;
source?: ErrorSource;
error?: Error;
fatal?: boolean;
}

export interface IErrorState {
Expand Down
11 changes: 9 additions & 2 deletions packages/platform-shared/src/shared/loader/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,20 @@ function redirectHelper(to: string, status: number = 302) {
throw createRedirect(to, status);
}

function errorHelper(msg?: string, statusCode: number = 500) {
function errorHelper(
msg?: string,
statusCode: number = 500,
{ fatal = false } = {}
) {
invariant(
statusCode >= 400 && statusCode < 600,
'status code should be 4xx and 5xx'
);

throw response(msg, { status: statusCode });
throw response(
{ error: true, fatal: fatal },
{ status: statusCode, statusText: msg }
);
}

export function createLoaderContext({
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-shared/src/shared/loader/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type RedirectFunction = (to: string, status?: number) => any;
export interface ErrorFunction {
(): any;
(msg: string): any;
(msg: string, statusCode?: number): any;
(msg: string, statusCode?: number, options?: { fatal?: boolean }): any;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/platform-shared/src/shared/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ if (supportIterator) {
(Headers.prototype as any)[Symbol.iterator] = Headers.prototype.entries;
}

export type ResponseType = 'text' | 'json' | 'redirect' | 'plain';
export type ResponseType = 'text' | 'json' | 'redirect' | 'raw';

export interface Response {
readonly data: string;
readonly data: any;
readonly status: number;
readonly statusText: string;
readonly headers: Headers;
Expand Down Expand Up @@ -158,7 +158,7 @@ export const isText = createTypeCreator('text');
export const isRedirect = createTypeCreator('redirect');

export function response(data: any, options?: ResponseOptions): Response {
return new ResponseImpl(data, 'plain', options);
return new ResponseImpl(data, 'raw', options);
}

export function json(data: any): Response {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const EmptyComponnetPath = resolvePkgFile(
);

function genRouteId(filepath: string) {
return createHash('md4').update(filepath).digest('hex').substr(0, 4);
return createHash('shake256').update(filepath).digest('hex').substr(0, 4);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
IServerPluginContext
} from '@shuvi/service';
import { sendHTML as originalSendHtml } from '@shuvi/service/lib/server/utils';
import { Response, isRedirect, isText } from '@shuvi/platform-shared/shared';
import {
Response,
isRedirect,
isResponse,
isText
} from '@shuvi/platform-shared/shared';
import { SERVER_REQUEST } from '@shuvi/shared/constants/trace';
import { IHandlePageRequest, RequestContext, ISendHtml } from '../serverHooks';
import { renderToHTML } from './renderToHTML';
Expand Down Expand Up @@ -69,6 +74,11 @@ function createPageHandler(serverPluginContext: IServerPluginContext) {
);
await sendHtml(textResp.data, { req, res });
sendHtmlHookTrace.stop();
} else if (isResponse(result)) {
const rawResp = result as Response;
res.statusCode = rawResp.status;
res.statusMessage = rawResp.statusText;
res.end(rawResp.data);
} else {
// should never reach here
throw new Error('Unexpected reponse type from renderToHTML');
Expand Down
3 changes: 2 additions & 1 deletion packages/platform-web/src/shuvi-app/app/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ export const createApp: CreateAppClient = ({
if (isResponse(error) && error.status >= 400 && error.status < 600) {
// client error has no status code
app.setError({
message: error.data
message: error.statusText,
fatal: error.data.fatal
});
next();
runLoadersTrace.setAttribute(
Expand Down
3 changes: 2 additions & 1 deletion packages/platform-web/src/shuvi-app/app/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export const createApp: CreateAppServer = options => {
if (isResponse(error) && error.status >= 400 && error.status < 600) {
app.setError({
code: error.status,
message: error.data
message: error.statusText,
fatal: error.data.fatal
});
next();
runLoadersTrace.setAttribute(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { redirect } from '@shuvi/platform-shared/shared';
import { redirect, response } from '@shuvi/platform-shared/shared';
import { SHUVI_ERROR } from '@shuvi/shared/constants';
import { SERVER_REQUEST } from '@shuvi/shared/constants/trace';
import { Router } from '@shuvi/router-react';
Expand All @@ -19,7 +19,12 @@ export class ReactServerView implements IReactServerView {
renderApp: IReactServerView['renderApp'] = async ({ req, app, manifest }) => {
await Loadable.preloadAll();

const { router, appComponent: AppComponent, setError: setAppError } = app;
const {
router,
appComponent: AppComponent,
setError: setAppError,
error
} = app;
await router.ready;

// todo: move these into renderer
Expand All @@ -29,6 +34,10 @@ export class ReactServerView implements IReactServerView {
setAppError(SHUVI_ERROR.PAGE_NOT_FOUND);
}

if (error && error.fatal) {
return response('', { status: error.code, statusText: error.message });
}

if (redirected) {
// handel loader redirect
if (
Expand Down
6 changes: 6 additions & 0 deletions test/e2e/loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ describe.each([false, true])('loader', hasBasename => {
expect(await page.$text('div')).toMatch('random_sdfsf_test_error');
});
});

it('should skip render when facing fatal-error', async () => {
page = await ctx.browser.page(ctx.url('/fatal-error'));
expect(page.statusCode).toBe(404);
expect(await page.$('[data-test-id="root-layout"]')).toBe(null);
});
});

describe('ssr = false', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/loader/shuvi.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineConfig } from 'shuvi';
export default defineConfig({
ssr: false,
ssr: true,
router: {
history: 'browser'
},
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/loader/src/routes/fatal-error/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { Loader } from '@shuvi/runtime';

export const loader: Loader<any> = async ctx => {
ctx.error('Not Found', 404, { fatal: true });
return null;
};

export default () => {
return null;
};

0 comments on commit f645b2e

Please sign in to comment.