@shuvi/platform-web 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ import pageLoaders from '@shuvi/app/files/page-loaders';
15
15
  import { historyMode } from '@shuvi/app/files/routerConfig';
16
16
  import { SHUVI_ERROR } from '@shuvi/shared/lib/constants';
17
17
  import { serializeServerError } from '../helper/serializeServerError';
18
+ import isThirdSite from '../helper/isThirdSite';
18
19
  let app;
19
20
  export const createApp = ({ routes, appData, appComponent }) => {
20
21
  // app is a singleton in client side
@@ -98,7 +99,16 @@ export const createApp = ({ routes, appData, appComponent }) => {
98
99
  }
99
100
  catch (error) {
100
101
  if (isRedirect(error)) {
101
- next(error.headers.get('Location'));
102
+ const location = error.headers.get('Location');
103
+ if (isThirdSite(location)) {
104
+ window.location.replace(location);
105
+ }
106
+ else {
107
+ next({
108
+ path: location,
109
+ replace: true
110
+ });
111
+ }
102
112
  return;
103
113
  }
104
114
  if (isResponse(error) && error.status >= 400 && error.status < 600) {
@@ -12,7 +12,7 @@ import { getRoutes, app as AppComponent } from '@shuvi/app/core/platform';
12
12
  import { runLoaders, getRouteMatchesWithInvalidLoader, isResponse, isRedirect } from '@shuvi/platform-shared/shared';
13
13
  import pageLoaders from '@shuvi/app/files/page-loaders';
14
14
  import application from '@shuvi/platform-shared/shuvi-app/application';
15
- import { createRouter, createMemoryHistory } from '@shuvi/router';
15
+ import { createRouter, createMemoryHistory, pathToString } from '@shuvi/router';
16
16
  import logger from '@shuvi/utils/lib/logger';
17
17
  import { serializeServerError } from '../helper/serializeServerError';
18
18
  export const createApp = options => {
@@ -39,7 +39,17 @@ export const createApp = options => {
39
39
  }
40
40
  catch (error) {
41
41
  if (isRedirect(error)) {
42
- next(error.headers.get('Location'));
42
+ const location = error.headers.get('Location');
43
+ const status = error.status;
44
+ next({
45
+ path: pathToString(to),
46
+ replace: true,
47
+ skipGuards: true,
48
+ state: {
49
+ location,
50
+ status
51
+ }
52
+ });
43
53
  return;
44
54
  }
45
55
  if (isResponse(error) && error.status >= 400 && error.status < 600) {
@@ -0,0 +1,2 @@
1
+ declare function isThirdSite(url: string): boolean;
2
+ export default isThirdSite;
@@ -0,0 +1,5 @@
1
+ const THIRD_SITE_REG = /^http(s?)\:\/\//;
2
+ function isThirdSite(url) {
3
+ return THIRD_SITE_REG.test(url);
4
+ }
5
+ export default isThirdSite;
@@ -12,11 +12,11 @@ function ErrorGuard({ children = null }) {
12
12
  return <>{children}</>;
13
13
  }
14
14
  export default function AppContainer({ app, children }) {
15
- return (<ErrorBoundary>
16
- <AppProvider app={app}>
15
+ return (<AppProvider app={app}>
16
+ <ErrorBoundary>
17
17
  <Provider store={app.store}>
18
18
  <ErrorGuard>{children}</ErrorGuard>
19
19
  </Provider>
20
- </AppProvider>
21
- </ErrorBoundary>);
20
+ </ErrorBoundary>
21
+ </AppProvider>);
22
22
  }
@@ -17,6 +17,7 @@ import Loadable, { LoadableContext } from '../loadable';
17
17
  import AppContainer from '../AppContainer';
18
18
  import { Head } from '../head';
19
19
  import { serializeServerError } from '../../helper/serializeServerError';
20
+ import isThirdSite from '../../helper/isThirdSite';
20
21
  export class ReactServerView {
21
22
  constructor() {
22
23
  this.renderApp = ({ req, app, manifest }) => __awaiter(this, void 0, void 0, function* () {
@@ -24,13 +25,16 @@ export class ReactServerView {
24
25
  const { router, appComponent: AppComponent, setError: setAppError } = app;
25
26
  yield router.ready;
26
27
  // todo: move these into renderer
27
- let { pathname, matches, redirected } = router.current;
28
+ let { matches, redirected, state, pathname } = router.current;
28
29
  // handler no matches
29
30
  if (!matches.length) {
30
31
  setAppError(SHUVI_ERROR.PAGE_NOT_FOUND);
31
32
  }
32
33
  if (redirected) {
33
- return redirect(pathname);
34
+ const { location, status } = state;
35
+ return redirect(isThirdSite(location)
36
+ ? location
37
+ : router.resolve(location, pathname).href, status);
34
38
  }
35
39
  const loadableModules = [];
36
40
  let htmlContent = undefined;
@@ -33,5 +33,9 @@ exports.default = (0, service_1.createServerPlugin)({
33
33
  modifyHtml: (document, context) => __awaiter(void 0, void 0, void 0, function* () {
34
34
  var _a, _b, _c;
35
35
  yield ((_c = (_b = (_a = resources_1.default.server) === null || _a === void 0 ? void 0 : _a.server) === null || _b === void 0 ? void 0 : _b.modifyHtml) === null || _c === void 0 ? void 0 : _c.call(_b, document, context));
36
+ }),
37
+ sendHtml: (originalSendHtml) => __awaiter(void 0, void 0, void 0, function* () {
38
+ var _d, _e;
39
+ return (((_e = (_d = resources_1.default.server.server) === null || _d === void 0 ? void 0 : _d.sendHtml) === null || _e === void 0 ? void 0 : _e.call(_d, originalSendHtml)) || originalSendHtml);
36
40
  })
37
41
  });
@@ -14,6 +14,11 @@ const utils_1 = require("@shuvi/service/lib/server/utils");
14
14
  const shared_1 = require("@shuvi/platform-shared/shared");
15
15
  const renderToHTML_1 = require("./renderToHTML");
16
16
  function createPageHandler(serverPluginContext) {
17
+ const wrappedSendHtml = (html, { req, res }) => __awaiter(this, void 0, void 0, function* () {
18
+ (0, utils_1.sendHTML)(req, res, html);
19
+ });
20
+ let sendHtml;
21
+ let pendingSendHtml;
17
22
  return function (req, res) {
18
23
  return __awaiter(this, void 0, void 0, function* () {
19
24
  const result = yield (0, renderToHTML_1.renderToHTML)({
@@ -30,7 +35,14 @@ function createPageHandler(serverPluginContext) {
30
35
  else if ((0, shared_1.isText)(result)) {
31
36
  const textResp = result;
32
37
  res.statusCode = textResp.status;
33
- (0, utils_1.sendHTML)(req, res, textResp.data);
38
+ if (!sendHtml) {
39
+ if (!pendingSendHtml) {
40
+ pendingSendHtml =
41
+ serverPluginContext.serverPluginRunner.sendHtml(wrappedSendHtml);
42
+ }
43
+ sendHtml = yield pendingSendHtml;
44
+ }
45
+ yield sendHtml(textResp.data, { req, res });
34
46
  }
35
47
  else {
36
48
  // shuold never reach here
@@ -43,10 +55,15 @@ function getPageMiddleware(api) {
43
55
  return __awaiter(this, void 0, void 0, function* () {
44
56
  const defaultPageHandler = createPageHandler(api);
45
57
  let pageHandler;
58
+ let pendingPageHandler;
46
59
  return function (req, res, next) {
47
60
  return __awaiter(this, void 0, void 0, function* () {
48
61
  if (!pageHandler) {
49
- pageHandler = yield api.serverPluginRunner.handlePageRequest(defaultPageHandler);
62
+ if (!pendingPageHandler) {
63
+ pendingPageHandler =
64
+ api.serverPluginRunner.handlePageRequest(defaultPageHandler);
65
+ }
66
+ pageHandler = yield pendingPageHandler;
50
67
  }
51
68
  try {
52
69
  yield pageHandler(req, res);
@@ -7,9 +7,15 @@ export interface ModifyHtmlContext {
7
7
  req: ShuviRequest;
8
8
  appContext: IAppContext;
9
9
  }
10
- export declare type IHandlePageRequest = (req: IncomingMessage, res: ServerResponse) => any;
10
+ export declare type IHandlePageRequest = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
11
+ export declare type RequestContext = {
12
+ req: IncomingMessage;
13
+ res: ServerResponse;
14
+ };
15
+ export declare type ISendHtml = (html: string, requestContext: RequestContext) => Promise<void>;
11
16
  export declare const extendedHooks: {
12
17
  getPageData: import("@shuvi/hook").AsyncParallelHook<void, IAppContext, Record<string, unknown>>;
13
- handlePageRequest: import("@shuvi/hook").SyncWaterfallHook<IHandlePageRequest, void>;
18
+ handlePageRequest: import("@shuvi/hook").AsyncSeriesWaterfallHook<IHandlePageRequest, void>;
14
19
  modifyHtml: import("@shuvi/hook").AsyncSeriesHook<IHtmlDocument, ModifyHtmlContext, void>;
20
+ sendHtml: import("@shuvi/hook").AsyncSeriesWaterfallHook<ISendHtml, void>;
15
21
  };
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extendedHooks = void 0;
4
4
  const hook_1 = require("@shuvi/hook");
5
5
  const getPageData = (0, hook_1.createAsyncParallelHook)();
6
- const handlePageRequest = (0, hook_1.createSyncWaterfallHook)();
6
+ const handlePageRequest = (0, hook_1.createAsyncSeriesWaterfallHook)();
7
7
  const modifyHtml = (0, hook_1.createAsyncSeriesHook)();
8
+ const sendHtml = (0, hook_1.createAsyncSeriesWaterfallHook)();
8
9
  exports.extendedHooks = {
9
10
  getPageData,
10
11
  handlePageRequest,
11
- modifyHtml
12
+ modifyHtml,
13
+ sendHtml
12
14
  };
@@ -7,6 +7,7 @@ declare global {
7
7
  getPageData: typeof extendedHooks.getPageData;
8
8
  handlePageRequest: typeof extendedHooks.handlePageRequest;
9
9
  modifyHtml: typeof extendedHooks.modifyHtml;
10
+ sendHtml: typeof extendedHooks.sendHtml;
10
11
  }
11
12
  }
12
13
  }
@@ -18,9 +19,11 @@ export declare type ShuviApiHandler = IApiRequestHandler;
18
19
  export declare type GetPageDataFunction = RemoveLast<ServerPluginConstructor['getPageData']>;
19
20
  export declare type HandlePageRequestFunction = RemoveLast<ServerPluginConstructor['handlePageRequest']>;
20
21
  export declare type ModifyHtmlFunction = RemoveLast<ServerPluginConstructor['modifyHtml']>;
22
+ export declare type SendHtmlFunction = RemoveLast<ServerPluginConstructor['sendHtml']>;
21
23
  export interface IServerModule {
22
24
  getPageData?: GetPageDataFunction;
23
25
  handlePageRequest?: HandlePageRequestFunction;
24
26
  modifyHtml?: ModifyHtmlFunction;
27
+ sendHtml?: SendHtmlFunction;
25
28
  }
26
29
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuvi/platform-web",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/shuvijs/shuvi.git",
@@ -72,18 +72,18 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@next/react-refresh-utils": "12.1.6",
75
- "@shuvi/error-overlay": "1.0.1",
76
- "@shuvi/hook": "1.0.1",
77
- "@shuvi/platform-shared": "1.0.1",
75
+ "@shuvi/error-overlay": "1.0.2",
76
+ "@shuvi/hook": "1.0.2",
77
+ "@shuvi/platform-shared": "1.0.2",
78
78
  "@shuvi/redox": "0.0.7",
79
79
  "@shuvi/redox-react": "0.0.7",
80
- "@shuvi/router": "1.0.1",
81
- "@shuvi/router-react": "1.0.1",
82
- "@shuvi/runtime": "1.0.1",
83
- "@shuvi/service": "1.0.1",
84
- "@shuvi/shared": "1.0.1",
85
- "@shuvi/toolpack": "1.0.1",
86
- "@shuvi/utils": "1.0.1",
80
+ "@shuvi/router": "1.0.2",
81
+ "@shuvi/router-react": "1.0.2",
82
+ "@shuvi/runtime": "1.0.2",
83
+ "@shuvi/service": "1.0.2",
84
+ "@shuvi/shared": "1.0.2",
85
+ "@shuvi/toolpack": "1.0.2",
86
+ "@shuvi/utils": "1.0.2",
87
87
  "content-type": "1.0.4",
88
88
  "core-js": "3.6.5",
89
89
  "ejs": "3.1.5",
@@ -99,7 +99,7 @@
99
99
  "whatwg-fetch": "3.0.0"
100
100
  },
101
101
  "peerDependencies": {
102
- "@shuvi/service": "1.0.1"
102
+ "@shuvi/service": "1.0.2"
103
103
  },
104
104
  "devDependencies": {
105
105
  "@testing-library/react": "^13.2.0",