@stone-js/use-react 0.1.0 → 0.3.0
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.
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/dist/browser.js +1794 -0
- package/dist/index.d.ts +279 -279
- package/dist/index.js +542 -695
- package/package.json +21 -15
package/dist/index.js
CHANGED
|
@@ -1,13 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InitializationError, isEmpty, isNotEmpty, isMetaClassModule, isMetaFactoryModule, isObjectLikeModule, isFunction, isFunctionModule, mergeBlueprints, stoneBlueprint, classDecoratorLegacyWrapper, setMetadata, methodDecoratorLegacyWrapper, addMetadata, LIFECYCLE_HOOK_KEY, hasMetadata, getMetadata, isMatchedAdapter, Logger, addBlueprint } from '@stone-js/core';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { renderToString } from 'react-dom/server';
|
|
4
|
-
import { createContext, StrictMode, useContext, useState, useEffect } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
import { OutgoingHttpResponse, RedirectResponse, MetaStaticFileMiddleware, MetaCompressionMiddleware } from '@stone-js/http-core';
|
|
7
|
-
import { OutgoingBrowserResponse, RedirectBrowserResponse } from '@stone-js/browser-core';
|
|
4
|
+
import { createContext, StrictMode, useContext, useMemo, useState, useEffect } from 'react';
|
|
5
|
+
import { hydrateRoot, createRoot } from 'react-dom/client';
|
|
8
6
|
import { Config } from '@stone-js/config';
|
|
9
|
-
import { GET,
|
|
10
|
-
import {
|
|
7
|
+
import { GET, NODE_CONSOLE_PLATFORM, Router, RouteEvent } from '@stone-js/router';
|
|
8
|
+
import { RedirectResponse, OutgoingHttpResponse, MetaCompressionMiddleware, MetaStaticFileMiddleware } from '@stone-js/http-core';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Custom error for react operations.
|
|
12
|
+
*/
|
|
13
|
+
class UseReactError extends InitializationError {
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = 'UseReactError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A useReact event handler for processing incoming events
|
|
22
|
+
* For single event handler.
|
|
23
|
+
*
|
|
24
|
+
* Multiple event handlers will be processed by the router.
|
|
25
|
+
*
|
|
26
|
+
* @template IncomingEventType - The type representing the incoming event.
|
|
27
|
+
* @template OutgoingResponseType - The type representing the outgoing response.
|
|
28
|
+
*/
|
|
29
|
+
class UseReactEventHandler {
|
|
30
|
+
blueprint;
|
|
31
|
+
/**
|
|
32
|
+
* Constructs a `UseReactEventHandler` instance.
|
|
33
|
+
*
|
|
34
|
+
* @param options - The UseReactEventHandler options including blueprint.
|
|
35
|
+
*/
|
|
36
|
+
constructor({ blueprint }) {
|
|
37
|
+
this.blueprint = blueprint;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handle an incoming event.
|
|
41
|
+
*
|
|
42
|
+
* @returns The outgoing response.
|
|
43
|
+
*/
|
|
44
|
+
handle() {
|
|
45
|
+
return this.getComponentEventHandler();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the component event handler.
|
|
49
|
+
*
|
|
50
|
+
* @returns The component event handler.
|
|
51
|
+
* @throws {UseReactError} If the component event handler is missing.
|
|
52
|
+
*/
|
|
53
|
+
getComponentEventHandler() {
|
|
54
|
+
const handler = this.blueprint.get('stone.useReact.componentEventHandler');
|
|
55
|
+
if (isEmpty(handler)) {
|
|
56
|
+
throw new UseReactError('The component event handler is missing.');
|
|
57
|
+
}
|
|
58
|
+
return handler;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
11
61
|
|
|
12
62
|
/**
|
|
13
63
|
* Stone DOM Attribute.
|
|
@@ -271,16 +321,6 @@ const StoneError = () => {
|
|
|
271
321
|
return (jsxs(Fragment, { children: [jsx("h1", { children: "An error occured" }), jsx("p", { children: "Sorry, something went wrong." })] }));
|
|
272
322
|
};
|
|
273
323
|
|
|
274
|
-
/**
|
|
275
|
-
* Custom error for react operations.
|
|
276
|
-
*/
|
|
277
|
-
class UseReactError extends InitializationError {
|
|
278
|
-
constructor(message, options) {
|
|
279
|
-
super(message, options);
|
|
280
|
-
this.name = 'UseReactError';
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
324
|
/**
|
|
285
325
|
* Build the React application for the current route.
|
|
286
326
|
* Or for the main handler if the route is not defined.
|
|
@@ -486,7 +526,7 @@ const htmlTemplate = (blueprint) => {
|
|
|
486
526
|
* @returns True if the application is running on the server side, false otherwise.
|
|
487
527
|
*/
|
|
488
528
|
function isSSR() {
|
|
489
|
-
return
|
|
529
|
+
return typeof window === 'undefined';
|
|
490
530
|
}
|
|
491
531
|
/**
|
|
492
532
|
* Execute the handler.
|
|
@@ -552,11 +592,11 @@ function getBrowserContent(app, component, layout, snapshot, head) {
|
|
|
552
592
|
* @param container - The service container.
|
|
553
593
|
* @param event - The incoming browser event.
|
|
554
594
|
* @param head - The head context.
|
|
555
|
-
* @returns
|
|
595
|
+
* @returns The server response content as a string.
|
|
556
596
|
*/
|
|
557
|
-
|
|
597
|
+
function getServerContent(component, data, container, event, head) {
|
|
558
598
|
const html = renderToString(component).concat('\n<!--app-html-->');
|
|
559
|
-
const template =
|
|
599
|
+
const template = htmlTemplate(container.make('blueprint'));
|
|
560
600
|
const snapshot = snapshotResponse(event, container, data).concat('\n<!--app-head-->');
|
|
561
601
|
return applyHeadContextToHtmlString(head ?? {}, template)
|
|
562
602
|
.replace('<!--app-html-->', html)
|
|
@@ -713,87 +753,30 @@ class ReactRuntime {
|
|
|
713
753
|
const MetaReactRuntime = { module: ReactRuntime, isClass: true, alias: 'reactRuntime', singleton: true };
|
|
714
754
|
|
|
715
755
|
/**
|
|
716
|
-
* Class representing an
|
|
756
|
+
* Class representing an UseReactUseReactKernelErrorHandler.
|
|
717
757
|
*
|
|
718
|
-
*
|
|
758
|
+
* Kernel level error handler for React applications.
|
|
719
759
|
*/
|
|
720
|
-
class
|
|
721
|
-
logger;
|
|
760
|
+
class UseReactKernelErrorHandler {
|
|
722
761
|
blueprint;
|
|
723
762
|
/**
|
|
724
|
-
* Create an
|
|
763
|
+
* Create an UseReactUseReactKernelErrorHandler.
|
|
725
764
|
*
|
|
726
|
-
* @param options -
|
|
765
|
+
* @param options - UseReactUseReactKernelErrorHandler options.
|
|
727
766
|
*/
|
|
728
767
|
constructor({ blueprint }) {
|
|
729
768
|
this.blueprint = blueprint;
|
|
730
|
-
this.logger = Logger.getInstance();
|
|
731
769
|
}
|
|
732
770
|
/**
|
|
733
771
|
* Handle an error.
|
|
734
772
|
*
|
|
735
773
|
* @param error - The error to handle.
|
|
736
|
-
* @
|
|
737
|
-
* @returns The raw response.
|
|
738
|
-
*/
|
|
739
|
-
async handle(error, context) {
|
|
740
|
-
this.logger.error(error.message, { error });
|
|
741
|
-
return context
|
|
742
|
-
.rawResponseBuilder
|
|
743
|
-
.add('render', async () => await this.renderError(error, context));
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* Get the error body.
|
|
747
|
-
*
|
|
748
|
-
* @param error - The error to handle.
|
|
749
|
-
* @returns The error body.
|
|
750
|
-
*/
|
|
751
|
-
async renderError(error, context) {
|
|
752
|
-
const app = await buildAdapterErrorComponent(this.blueprint, context, error.statusCode ?? 500, error);
|
|
753
|
-
// Render the component
|
|
754
|
-
renderReactApp(app, this.blueprint);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
/**
|
|
759
|
-
* A useReact event handler for processing incoming events
|
|
760
|
-
* For single event handler.
|
|
761
|
-
*
|
|
762
|
-
* Multiple event handlers will be processed by the router.
|
|
763
|
-
*
|
|
764
|
-
* @template IncomingEventType - The type representing the incoming event.
|
|
765
|
-
* @template OutgoingResponseType - The type representing the outgoing response.
|
|
766
|
-
*/
|
|
767
|
-
class UseReactEventHandler {
|
|
768
|
-
blueprint;
|
|
769
|
-
/**
|
|
770
|
-
* Constructs a `UseReactEventHandler` instance.
|
|
771
|
-
*
|
|
772
|
-
* @param options - The UseReactEventHandler options including blueprint.
|
|
773
|
-
*/
|
|
774
|
-
constructor({ blueprint }) {
|
|
775
|
-
this.blueprint = blueprint;
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Handle an incoming event.
|
|
779
|
-
*
|
|
780
|
-
* @returns The outgoing response.
|
|
781
|
-
*/
|
|
782
|
-
handle() {
|
|
783
|
-
return this.getComponentEventHandler();
|
|
784
|
-
}
|
|
785
|
-
/**
|
|
786
|
-
* Get the component event handler.
|
|
787
|
-
*
|
|
788
|
-
* @returns The component event handler.
|
|
789
|
-
* @throws {UseReactError} If the component event handler is missing.
|
|
774
|
+
* @returns The outgoing http response.
|
|
790
775
|
*/
|
|
791
|
-
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
}
|
|
796
|
-
return handler;
|
|
776
|
+
handle(error) {
|
|
777
|
+
const metavalue = this.blueprint.get(`stone.useReact.errorPages.${String(error?.name)}`) ??
|
|
778
|
+
this.blueprint.get('stone.useReact.errorPages.default', {});
|
|
779
|
+
return { content: { ...metavalue, error }, statusCode: error?.statusCode ?? 500 };
|
|
797
780
|
}
|
|
798
781
|
}
|
|
799
782
|
|
|
@@ -819,7 +802,7 @@ async function preparePage(event, response, container, snapshot) {
|
|
|
819
802
|
const component = await buildPageComponent(event, container, componentType, data, response.statusCode);
|
|
820
803
|
const appComponent = await buildAppComponent(event, container, componentType, layout, data, response.statusCode);
|
|
821
804
|
response.setContent(isSSR()
|
|
822
|
-
?
|
|
805
|
+
? getServerContent(appComponent, snapshotData, container, event, head)
|
|
823
806
|
: getBrowserContent(appComponent, component, layout, snapshot, head));
|
|
824
807
|
}
|
|
825
808
|
/**
|
|
@@ -845,7 +828,7 @@ async function prepareErrorPage(event, response, container, snapshot) {
|
|
|
845
828
|
const component = await buildPageComponent(event, container, componentType, data, response.statusCode, error);
|
|
846
829
|
const appComponent = await buildAppComponent(event, container, componentType, layout, data, response.statusCode, error);
|
|
847
830
|
response.setContent(isSSR()
|
|
848
|
-
?
|
|
831
|
+
? getServerContent(appComponent, snapshotData, container, event, head)
|
|
849
832
|
: getBrowserContent(appComponent, component, layout, snapshot, head));
|
|
850
833
|
}
|
|
851
834
|
/**
|
|
@@ -888,108 +871,6 @@ async function onPreparingResponse({ event, response, container }) {
|
|
|
888
871
|
}
|
|
889
872
|
}
|
|
890
873
|
|
|
891
|
-
/**
|
|
892
|
-
* Class representing an UseReactUseReactKernelErrorHandler.
|
|
893
|
-
*
|
|
894
|
-
* Kernel level error handler for React applications.
|
|
895
|
-
*/
|
|
896
|
-
class UseReactKernelErrorHandler {
|
|
897
|
-
blueprint;
|
|
898
|
-
/**
|
|
899
|
-
* Create an UseReactUseReactKernelErrorHandler.
|
|
900
|
-
*
|
|
901
|
-
* @param options - UseReactUseReactKernelErrorHandler options.
|
|
902
|
-
*/
|
|
903
|
-
constructor({ blueprint }) {
|
|
904
|
-
this.blueprint = blueprint;
|
|
905
|
-
}
|
|
906
|
-
/**
|
|
907
|
-
* Handle an error.
|
|
908
|
-
*
|
|
909
|
-
* @param error - The error to handle.
|
|
910
|
-
* @returns The outgoing http response.
|
|
911
|
-
*/
|
|
912
|
-
handle(error) {
|
|
913
|
-
const metavalue = this.blueprint.get(`stone.useReact.errorPages.${String(error?.name)}`) ??
|
|
914
|
-
this.blueprint.get('stone.useReact.errorPages.default', {});
|
|
915
|
-
return { content: { ...metavalue, error }, statusCode: error?.statusCode ?? 500 };
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
/**
|
|
920
|
-
* Create an UseReact response.
|
|
921
|
-
*
|
|
922
|
-
* @param options - The options for creating the response.
|
|
923
|
-
* @returns The React response.
|
|
924
|
-
*/
|
|
925
|
-
const reactResponse = async (options) => {
|
|
926
|
-
if (isNotEmpty(options) &&
|
|
927
|
-
(isNotEmpty(options.url) ||
|
|
928
|
-
(isNotEmpty(options.content) && isNotEmpty(options.content.redirect)))) {
|
|
929
|
-
return await reactRedirectResponse(options);
|
|
930
|
-
}
|
|
931
|
-
return import.meta.env.SSR
|
|
932
|
-
? OutgoingHttpResponse.create(options)
|
|
933
|
-
: OutgoingBrowserResponse.create(options);
|
|
934
|
-
};
|
|
935
|
-
/**
|
|
936
|
-
* Create an UseReact redirect response.
|
|
937
|
-
*
|
|
938
|
-
* @param options - The options for creating the response.
|
|
939
|
-
* @returns The React redirect response.
|
|
940
|
-
*/
|
|
941
|
-
const reactRedirectResponse = async (options) => {
|
|
942
|
-
return import.meta.env.SSR
|
|
943
|
-
? RedirectResponse.create({ statusCode: 302, ...options })
|
|
944
|
-
: RedirectBrowserResponse.create({ statusCode: 302, ...options });
|
|
945
|
-
};
|
|
946
|
-
|
|
947
|
-
/**
|
|
948
|
-
* Class representing an UseReactServerErrorHandler.
|
|
949
|
-
*
|
|
950
|
-
* Adapter level error handler for React applications.
|
|
951
|
-
*/
|
|
952
|
-
class UseReactServerErrorHandler {
|
|
953
|
-
logger;
|
|
954
|
-
blueprint;
|
|
955
|
-
/**
|
|
956
|
-
* Create an UseReactServerErrorHandler.
|
|
957
|
-
*
|
|
958
|
-
* @param options - UseReactServerErrorHandler options.
|
|
959
|
-
*/
|
|
960
|
-
constructor({ blueprint }) {
|
|
961
|
-
this.blueprint = blueprint;
|
|
962
|
-
this.logger = Logger.getInstance();
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Handle an error.
|
|
966
|
-
*
|
|
967
|
-
* @param error - The error to handle.
|
|
968
|
-
* @param context - The context of the adapter.
|
|
969
|
-
* @returns The raw response.
|
|
970
|
-
*/
|
|
971
|
-
async handle(error, context) {
|
|
972
|
-
this.logger.error(error.message, { error });
|
|
973
|
-
return context
|
|
974
|
-
.rawResponseBuilder
|
|
975
|
-
.add('statusCode', error.statusCode ?? 500)
|
|
976
|
-
.add('headers', new Headers({ 'Content-Type': 'text/html' }))
|
|
977
|
-
.add('body', await this.getErrorBody(error, context));
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* Get the error body.
|
|
981
|
-
*
|
|
982
|
-
* @param error - The error to handle.
|
|
983
|
-
* @returns The error body.
|
|
984
|
-
*/
|
|
985
|
-
async getErrorBody(error, context) {
|
|
986
|
-
const statusCode = error.statusCode ?? 500;
|
|
987
|
-
const template = await htmlTemplate(this.blueprint);
|
|
988
|
-
const ClientApp = await buildAdapterErrorComponent(this.blueprint, context, statusCode, error);
|
|
989
|
-
return template.replace('<!--app-html-->', renderToString(ClientApp));
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
874
|
/**
|
|
994
875
|
* Use React Service Provider.
|
|
995
876
|
*/
|
|
@@ -1059,39 +940,93 @@ function defineAdapterErrorPage(module, options) {
|
|
|
1059
940
|
}
|
|
1060
941
|
|
|
1061
942
|
/**
|
|
1062
|
-
*
|
|
943
|
+
* Default blueprint for a React-based Stone.js application.
|
|
1063
944
|
*
|
|
1064
|
-
*
|
|
1065
|
-
* @param options - Page definition options.
|
|
1066
|
-
* @returns The UseReactBlueprint.
|
|
945
|
+
* - Defines middleware, lifecycle hooks, and the default HTML template path.
|
|
1067
946
|
*/
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
947
|
+
const internalUseReactBlueprint = {
|
|
948
|
+
stone: {
|
|
949
|
+
useReact: {},
|
|
950
|
+
services: [MetaReactRuntime],
|
|
951
|
+
providers: [MetaUseReactServiceProvider]
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Defines a Stone React app using a factory-based or class-based main handler.
|
|
957
|
+
*
|
|
958
|
+
* @param moduleOrOptions - A factory function or class constructor for the main page.
|
|
959
|
+
* @param optionsOrBlueprints - Optional application-level configuration.
|
|
960
|
+
* @param maybeBlueprints - Additional blueprints to merge.
|
|
961
|
+
* @returns A fully merged Stone blueprint.
|
|
962
|
+
*/
|
|
963
|
+
function defineStoneReactApp(moduleOrOptions = {}, optionsOrBlueprints, maybeBlueprints) {
|
|
964
|
+
let module;
|
|
965
|
+
let options = {};
|
|
966
|
+
let blueprints = [];
|
|
967
|
+
// Pattern: defineStoneReactApp(handler, options?, blueprints?)
|
|
968
|
+
if (isFunctionModule(moduleOrOptions)) {
|
|
969
|
+
module = moduleOrOptions;
|
|
970
|
+
if (isObjectLikeModule(optionsOrBlueprints)) {
|
|
971
|
+
options = optionsOrBlueprints;
|
|
972
|
+
blueprints = Array.isArray(maybeBlueprints) ? maybeBlueprints : [];
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
else if (isObjectLikeModule(moduleOrOptions)) { // Pattern: defineStoneReactApp(options, blueprints?)
|
|
976
|
+
options = moduleOrOptions;
|
|
977
|
+
blueprints = Array.isArray(optionsOrBlueprints) ? optionsOrBlueprints : [];
|
|
978
|
+
}
|
|
979
|
+
const stonePart = {
|
|
980
|
+
...options,
|
|
981
|
+
useReact: {
|
|
982
|
+
...options.useReact
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
if (isNotEmpty(module)) {
|
|
986
|
+
stonePart.useReact.componentEventHandler = {
|
|
987
|
+
module,
|
|
988
|
+
isComponent: true,
|
|
989
|
+
isClass: options.isClass,
|
|
990
|
+
isFactory: options.isClass !== true
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
return mergeBlueprints(stoneBlueprint, internalUseReactBlueprint, ...blueprints, { stone: stonePart });
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Utility function to define a page.
|
|
998
|
+
*
|
|
999
|
+
* @param module - The EventHandler module.
|
|
1000
|
+
* @param options - Page definition options.
|
|
1001
|
+
* @returns The UseReactBlueprint.
|
|
1002
|
+
*/
|
|
1003
|
+
function definePage(module, options) {
|
|
1004
|
+
return {
|
|
1005
|
+
stone: {
|
|
1006
|
+
router: {
|
|
1007
|
+
definitions: [
|
|
1008
|
+
{
|
|
1009
|
+
...options,
|
|
1010
|
+
method: GET,
|
|
1011
|
+
methods: [],
|
|
1012
|
+
children: undefined,
|
|
1013
|
+
handler: {
|
|
1014
|
+
module,
|
|
1015
|
+
isComponent: true,
|
|
1016
|
+
layout: options?.layout,
|
|
1017
|
+
isClass: options?.isClass,
|
|
1018
|
+
isFactory: options?.isClass !== true
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
]
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Utility function to define a page layout.
|
|
1028
|
+
*
|
|
1029
|
+
* @param module - The layout module.
|
|
1095
1030
|
* @param options - Optional page layout definition options.
|
|
1096
1031
|
* @returns The UseReactBlueprint.
|
|
1097
1032
|
*/
|
|
@@ -1164,595 +1099,503 @@ const REACT_ADAPTER_ERROR_PAGE_KEY = Symbol.for('ReactAdapterErrorPage');
|
|
|
1164
1099
|
const STONE_REACT_APP_KEY = Symbol.for('StoneReactApp');
|
|
1165
1100
|
|
|
1166
1101
|
/**
|
|
1167
|
-
*
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
*
|
|
1184
|
-
* @param context - The adapter context containing the raw event, execution context, and other data.
|
|
1185
|
-
* @param next - The next middleware to be invoked in the pipeline.
|
|
1186
|
-
* @returns A promise resolving to the processed context.
|
|
1187
|
-
* @throws {NodeHttpAdapterError} If required components are missing in the context.
|
|
1188
|
-
*/
|
|
1189
|
-
async handle(context, next) {
|
|
1190
|
-
const rawResponseBuilder = await next(context);
|
|
1191
|
-
this.ensureValidContext(context, rawResponseBuilder);
|
|
1192
|
-
return rawResponseBuilder.add('render', async () => await this.renderComponent(context.outgoingResponse));
|
|
1193
|
-
}
|
|
1194
|
-
/**
|
|
1195
|
-
* Ensures the context and response builder have the required components.
|
|
1196
|
-
*/
|
|
1197
|
-
ensureValidContext(context, rawResponseBuilder) {
|
|
1198
|
-
if (context.rawEvent === undefined ||
|
|
1199
|
-
context.incomingEvent === undefined ||
|
|
1200
|
-
context.outgoingResponse === undefined ||
|
|
1201
|
-
rawResponseBuilder?.add === undefined) {
|
|
1202
|
-
throw new UseReactError('The context is missing required components.');
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
/**
|
|
1206
|
-
* Renders the provided React content.
|
|
1207
|
-
*
|
|
1208
|
-
* @param response - The response object.
|
|
1209
|
-
*/
|
|
1210
|
-
async renderComponent(response) {
|
|
1211
|
-
if (isEmpty(response)) {
|
|
1212
|
-
throw new UseReactError('No response provided for rendering.');
|
|
1213
|
-
}
|
|
1214
|
-
const content = response.content;
|
|
1215
|
-
const targetUrl = response.targetUrl;
|
|
1216
|
-
if (isNotEmpty(targetUrl)) {
|
|
1217
|
-
return this.handleRedirect(targetUrl);
|
|
1218
|
-
}
|
|
1219
|
-
if (isNotEmpty(content?.head)) {
|
|
1220
|
-
applyHeadContextToDom(document, content.head);
|
|
1221
|
-
}
|
|
1222
|
-
if (content?.ssr === true && !this.isRendered && isNotEmpty(content?.app)) {
|
|
1223
|
-
return this.hydrateReactApp(content.app);
|
|
1224
|
-
}
|
|
1225
|
-
if (isNotEmpty(content?.app) && (!this.isRendered || content?.fullRender === true)) {
|
|
1226
|
-
return this.renderReactApp(content.app);
|
|
1227
|
-
}
|
|
1228
|
-
if (isNotEmpty(content?.component)) {
|
|
1229
|
-
return await this.dispatchComponentToOutlet(content.component);
|
|
1230
|
-
}
|
|
1231
|
-
throw new UseReactError('Invalid content provided for rendering.');
|
|
1232
|
-
}
|
|
1233
|
-
/**
|
|
1234
|
-
* Handles navigation redirection.
|
|
1235
|
-
*
|
|
1236
|
-
* @param path - The path to redirect to.
|
|
1237
|
-
*/
|
|
1238
|
-
handleRedirect(path) {
|
|
1239
|
-
window.history.pushState({ path }, '', path);
|
|
1240
|
-
window.dispatchEvent(new Event(NAVIGATION_EVENT));
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Hydrates the React app when SSR is enabled.
|
|
1244
|
-
*
|
|
1245
|
-
* @param app - The React app to hydrate.
|
|
1246
|
-
*/
|
|
1247
|
-
hydrateReactApp(app) {
|
|
1248
|
-
hydrateReactApp(app, this.blueprint);
|
|
1249
|
-
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Renders the React app.
|
|
1252
|
-
*
|
|
1253
|
-
* @param app - The React app to render.
|
|
1254
|
-
*/
|
|
1255
|
-
renderReactApp(app) {
|
|
1256
|
-
renderReactApp(app, this.blueprint);
|
|
1257
|
-
}
|
|
1258
|
-
/**
|
|
1259
|
-
* Dispatches a component to the layout outlet when no layout is defined.
|
|
1260
|
-
*
|
|
1261
|
-
* @param component - The component to dispatch.
|
|
1262
|
-
*/
|
|
1263
|
-
async dispatchComponentToOutlet(component) {
|
|
1264
|
-
window.dispatchEvent(new CustomEvent(STONE_PAGE_EVENT_OUTLET, { detail: component }));
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Meta Middleware for processing browser responses.
|
|
1102
|
+
* A class decorator for defining a class as a React Handler layout.
|
|
1103
|
+
*
|
|
1104
|
+
* @param options - Configuration options for the layout definition.
|
|
1105
|
+
* @returns A method decorator to be applied to a class method.
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```typescript
|
|
1109
|
+
* import { ErrorPage } from '@stone-js/use-react';
|
|
1110
|
+
*
|
|
1111
|
+
* @ErrorPage({ error: 'UserNotFoundError' })
|
|
1112
|
+
* class UserErrorPage {
|
|
1113
|
+
* render({ error }) {
|
|
1114
|
+
* return <h1>User name: {error.message}</h1>;
|
|
1115
|
+
* }
|
|
1116
|
+
* }
|
|
1117
|
+
* ```
|
|
1269
1118
|
*/
|
|
1270
|
-
const
|
|
1119
|
+
const ErrorPage = (options) => {
|
|
1120
|
+
return classDecoratorLegacyWrapper((_target, context) => {
|
|
1121
|
+
setMetadata(context, REACT_ERROR_PAGE_KEY, { ...options, isClass: true });
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1271
1124
|
|
|
1272
1125
|
/**
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
1275
|
-
* @param context - The configuration context containing modules and blueprint.
|
|
1276
|
-
* @param next - The next pipeline function to continue processing.
|
|
1277
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1126
|
+
* Hook decorator to mark a method as a lifecycle hook
|
|
1127
|
+
* And automatically add it to the global lifecycle hook registry.
|
|
1278
1128
|
*
|
|
1279
1129
|
* @example
|
|
1280
1130
|
* ```typescript
|
|
1281
|
-
*
|
|
1131
|
+
* class MyClass {
|
|
1132
|
+
* // ...
|
|
1133
|
+
* @Hook('onPreparingPage')
|
|
1134
|
+
* onPreparingPage () {}
|
|
1135
|
+
* }
|
|
1282
1136
|
* ```
|
|
1137
|
+
*
|
|
1138
|
+
* @param name - The name of the lifecycle hook.
|
|
1139
|
+
* @returns A class decorator function that sets the metadata using the provided options.
|
|
1283
1140
|
*/
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
context
|
|
1287
|
-
|
|
1288
|
-
.add('stone.lifecycleHooks.onPreparingResponse', [onPreparingResponse]);
|
|
1289
|
-
}
|
|
1290
|
-
return next(context);
|
|
1141
|
+
const Hook = (name) => {
|
|
1142
|
+
return methodDecoratorLegacyWrapper((_target, context) => {
|
|
1143
|
+
addMetadata(context, LIFECYCLE_HOOK_KEY, { name, method: context.name });
|
|
1144
|
+
});
|
|
1291
1145
|
};
|
|
1146
|
+
|
|
1292
1147
|
/**
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* The MetaBrowserResponseMiddleware is an adapter middleware and is useful
|
|
1296
|
-
* for handling outgoing responses and rendering them in the browser.
|
|
1148
|
+
* A class decorator for defining a class as a React Page layout.
|
|
1297
1149
|
*
|
|
1298
|
-
* @param
|
|
1299
|
-
* @
|
|
1300
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1150
|
+
* @param options - Configuration options for the layout definition.
|
|
1151
|
+
* @returns A method decorator to be applied to a class method.
|
|
1301
1152
|
*
|
|
1302
1153
|
* @example
|
|
1303
1154
|
* ```typescript
|
|
1304
|
-
*
|
|
1155
|
+
* import { PageLayout } from '@stone-js/use-react';
|
|
1156
|
+
*
|
|
1157
|
+
* @PageLayout({ name: 'UserPageLayout' })
|
|
1158
|
+
* class UserPageLayout {
|
|
1159
|
+
* render({ data }) {
|
|
1160
|
+
* return <h1>User name: {data.name}</h1>;
|
|
1161
|
+
* }
|
|
1162
|
+
* }
|
|
1305
1163
|
* ```
|
|
1306
1164
|
*/
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
context
|
|
1310
|
-
}
|
|
1311
|
-
return await next(context);
|
|
1165
|
+
const PageLayout = (options) => {
|
|
1166
|
+
return classDecoratorLegacyWrapper((_target, context) => {
|
|
1167
|
+
setMetadata(context, REACT_PAGE_LAYOUT_KEY, { ...options, isClass: true });
|
|
1168
|
+
});
|
|
1312
1169
|
};
|
|
1170
|
+
|
|
1313
1171
|
/**
|
|
1314
|
-
*
|
|
1172
|
+
* A class decorator for defining a class as a React Handler layout.
|
|
1315
1173
|
*
|
|
1316
|
-
* @param
|
|
1317
|
-
* @
|
|
1318
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1174
|
+
* @param options - Configuration options for the layout definition.
|
|
1175
|
+
* @returns A method decorator to be applied to a class method.
|
|
1319
1176
|
*
|
|
1320
1177
|
* @example
|
|
1321
1178
|
* ```typescript
|
|
1322
|
-
*
|
|
1179
|
+
* import { AdapterErrorPage } from '@stone-js/use-react';
|
|
1180
|
+
*
|
|
1181
|
+
* @AdapterErrorPage({ error: 'UserNotFoundError' })
|
|
1182
|
+
* class UserAdapterErrorPage {
|
|
1183
|
+
* render({ error }) {
|
|
1184
|
+
* return <h1>User name: {error.message}</h1>;
|
|
1185
|
+
* }
|
|
1186
|
+
* }
|
|
1323
1187
|
* ```
|
|
1324
1188
|
*/
|
|
1325
|
-
const
|
|
1326
|
-
context
|
|
1327
|
-
|
|
1328
|
-
.set('stone.kernel.errorHandlers.default', { module: UseReactKernelErrorHandler, isClass: true });
|
|
1329
|
-
context
|
|
1330
|
-
.modules
|
|
1331
|
-
.filter(module => hasMetadata(module, REACT_ERROR_PAGE_KEY))
|
|
1332
|
-
.forEach(module => {
|
|
1333
|
-
const { error, layout } = getMetadata(module, REACT_ERROR_PAGE_KEY, { error: 'default' });
|
|
1334
|
-
Array(error).flat().forEach(name => {
|
|
1335
|
-
context
|
|
1336
|
-
.blueprint
|
|
1337
|
-
.set(`stone.useReact.errorPages.${name}`, { layout, module, isClass: true });
|
|
1338
|
-
});
|
|
1339
|
-
});
|
|
1340
|
-
// Process both eager and lazy loaded error pages
|
|
1341
|
-
Object
|
|
1342
|
-
.keys(context.blueprint.get('stone.useReact.errorPages', {}))
|
|
1343
|
-
.forEach((name) => {
|
|
1344
|
-
context
|
|
1345
|
-
.blueprint
|
|
1346
|
-
.set(`stone.kernel.errorHandlers.${name}`, { module: UseReactKernelErrorHandler, isClass: true });
|
|
1189
|
+
const AdapterErrorPage = (options) => {
|
|
1190
|
+
return classDecoratorLegacyWrapper((_target, context) => {
|
|
1191
|
+
setMetadata(context, REACT_ADAPTER_ERROR_PAGE_KEY, { ...options, isClass: true });
|
|
1347
1192
|
});
|
|
1348
|
-
return next(context);
|
|
1349
1193
|
};
|
|
1194
|
+
|
|
1350
1195
|
/**
|
|
1351
|
-
*
|
|
1196
|
+
* Decorator to set the status code of the response.
|
|
1352
1197
|
*
|
|
1353
|
-
* @param
|
|
1354
|
-
* @param
|
|
1355
|
-
* @returns
|
|
1198
|
+
* @param statusCode - The status code of the response.
|
|
1199
|
+
* @param headers - The headers for the response.
|
|
1200
|
+
* @returns A method decorator.
|
|
1356
1201
|
*
|
|
1357
1202
|
* @example
|
|
1358
1203
|
* ```typescript
|
|
1359
|
-
*
|
|
1204
|
+
* import { Page, PageStatus } from '@stone-js/use-react';
|
|
1205
|
+
*
|
|
1206
|
+
* @Page('/user-profile')
|
|
1207
|
+
* class UserPage {
|
|
1208
|
+
* @PageStatus()
|
|
1209
|
+
* handle() {
|
|
1210
|
+
* return { name: 'John Doe' };
|
|
1211
|
+
* }
|
|
1212
|
+
* }
|
|
1360
1213
|
* ```
|
|
1361
1214
|
*/
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
.set('stone.adapter.errorHandlers.default', { module: UseReactAdapterErrorHandler, isClass: true });
|
|
1369
|
-
context
|
|
1370
|
-
.modules
|
|
1371
|
-
.filter(module => hasMetadata(module, REACT_ADAPTER_ERROR_PAGE_KEY))
|
|
1372
|
-
.forEach(module => {
|
|
1373
|
-
const { error, layout, adapterAlias, platform } = getMetadata(module, REACT_ADAPTER_ERROR_PAGE_KEY, { error: 'default' });
|
|
1374
|
-
if (isMatchedAdapter(context.blueprint, platform, adapterAlias)) {
|
|
1375
|
-
Array(error).flat().forEach(name => {
|
|
1376
|
-
context
|
|
1377
|
-
.blueprint
|
|
1378
|
-
.set(`stone.useReact.adapterErrorPages.${name}`, { isClass: true, layout, module });
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
});
|
|
1382
|
-
// Process both eager and lazy loaded error pages
|
|
1383
|
-
Object
|
|
1384
|
-
.keys(context.blueprint.get('stone.useReact.adapterErrorPages', {}))
|
|
1385
|
-
.forEach((name) => {
|
|
1386
|
-
context
|
|
1387
|
-
.blueprint
|
|
1388
|
-
.set(`stone.adapter.errorHandlers.${name}`, { module: UseReactAdapterErrorHandler, isClass: true });
|
|
1215
|
+
const PageStatus = (statusCode = 200, headers = {}) => {
|
|
1216
|
+
return methodDecoratorLegacyWrapper((target, _context) => {
|
|
1217
|
+
return async function (...args) {
|
|
1218
|
+
const content = await target.apply(this, args);
|
|
1219
|
+
return { content, statusCode, headers };
|
|
1220
|
+
};
|
|
1389
1221
|
});
|
|
1390
|
-
return next(context);
|
|
1391
1222
|
};
|
|
1223
|
+
|
|
1392
1224
|
/**
|
|
1393
|
-
*
|
|
1225
|
+
* A class decorator for defining a class as a React Page route action.
|
|
1226
|
+
* Uses the `Match` decorator internally to register the route with the HTTP `GET` method.
|
|
1394
1227
|
*
|
|
1395
|
-
* @param
|
|
1396
|
-
* @
|
|
1397
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1228
|
+
* @param options - Configuration options for the route definition, excluding the `methods` property.
|
|
1229
|
+
* @returns A method decorator to be applied to a class method.
|
|
1398
1230
|
*
|
|
1399
1231
|
* @example
|
|
1400
1232
|
* ```typescript
|
|
1401
|
-
*
|
|
1402
|
-
* ```
|
|
1403
|
-
*/
|
|
1404
|
-
const SetSSRStaticFileMiddleware = async (context, next) => {
|
|
1405
|
-
import.meta.env.SSR && context.blueprint.add('stone.kernel.middleware', [MetaStaticFileMiddleware]);
|
|
1406
|
-
return await next(context);
|
|
1407
|
-
};
|
|
1408
|
-
/**
|
|
1409
|
-
* Blueprint middleware to set CompressionMiddleware for SSR adapter.
|
|
1233
|
+
* import { Page } from '@stone-js/use-react';
|
|
1410
1234
|
*
|
|
1411
|
-
* @
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1235
|
+
* @Page('/user-profile')
|
|
1236
|
+
* class UserPage {
|
|
1237
|
+
* handle({ event }): Record<string, string> {
|
|
1238
|
+
* return { name: 'Jane Doe' };
|
|
1239
|
+
* }
|
|
1414
1240
|
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
*
|
|
1241
|
+
* render({ data }) {
|
|
1242
|
+
* return <h1>User name: {data.name}</h1>;
|
|
1243
|
+
* }
|
|
1244
|
+
* }
|
|
1418
1245
|
* ```
|
|
1419
1246
|
*/
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1247
|
+
const Page = (path, options = {}) => {
|
|
1248
|
+
return classDecoratorLegacyWrapper((target, context) => {
|
|
1249
|
+
setMetadata(context, REACT_PAGE_KEY, {
|
|
1250
|
+
...options,
|
|
1251
|
+
path,
|
|
1252
|
+
method: GET,
|
|
1253
|
+
methods: [],
|
|
1254
|
+
handler: { isClass: true, isComponent: true, layout: options.layout, module: target }
|
|
1255
|
+
});
|
|
1256
|
+
});
|
|
1423
1257
|
};
|
|
1258
|
+
|
|
1424
1259
|
/**
|
|
1425
|
-
*
|
|
1260
|
+
* Decorator to create a snapshot of the current data.
|
|
1426
1261
|
*
|
|
1427
|
-
* @param
|
|
1428
|
-
* @
|
|
1429
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1262
|
+
* @param name - The name of the snapshot.
|
|
1263
|
+
* @returns A method decorator.
|
|
1430
1264
|
*
|
|
1431
1265
|
* @example
|
|
1432
1266
|
* ```typescript
|
|
1433
|
-
*
|
|
1267
|
+
* import { Service } from '@stone-js/core';
|
|
1268
|
+
* import { Snapshot } from '@stone-js/use-react';
|
|
1269
|
+
*
|
|
1270
|
+
* @Service({ alias: 'userService' })
|
|
1271
|
+
* class UserService {
|
|
1272
|
+
* @Snapshot()
|
|
1273
|
+
* showProfile() {
|
|
1274
|
+
* return { name: 'John Doe' };
|
|
1275
|
+
* }
|
|
1276
|
+
* }
|
|
1434
1277
|
* ```
|
|
1435
1278
|
*/
|
|
1436
|
-
const
|
|
1437
|
-
context
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
const options = getMetadata(module, REACT_PAGE_KEY, { path: '/' });
|
|
1442
|
-
const definition = {
|
|
1443
|
-
...options,
|
|
1444
|
-
handler: { ...options.handler, module }
|
|
1279
|
+
const Snapshot = (name) => {
|
|
1280
|
+
return methodDecoratorLegacyWrapper((target, context) => {
|
|
1281
|
+
return async function (...args) {
|
|
1282
|
+
name = name ?? `${String(Object.getPrototypeOf(this).constructor.name)}.${String(context.name)}`;
|
|
1283
|
+
return await ReactRuntime.instance?.snapshot(name, () => target.apply(this, args));
|
|
1445
1284
|
};
|
|
1446
|
-
context.blueprint.add('stone.router.definitions', [definition]);
|
|
1447
1285
|
});
|
|
1448
|
-
return next(context);
|
|
1449
1286
|
};
|
|
1287
|
+
|
|
1450
1288
|
/**
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1453
|
-
* @param context - The configuration context containing modules and blueprint.
|
|
1454
|
-
* @param next - The next pipeline function to continue processing.
|
|
1455
|
-
* @returns The updated blueprint or a promise resolving to it.
|
|
1289
|
+
* Sets the error handler for the React adapter and registers error pages.
|
|
1456
1290
|
*
|
|
1457
|
-
* @
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1460
|
-
* ```
|
|
1291
|
+
* @param errorHandler - The error handler to set for the React adapter.
|
|
1292
|
+
* @param context - The blueprint context containing modules and blueprint.
|
|
1293
|
+
* @returns The updated blueprint context with the error handler and error pages set.
|
|
1461
1294
|
*/
|
|
1462
|
-
|
|
1295
|
+
function setUseReactAdapterErrorHandler(errorHandler, context) {
|
|
1296
|
+
context
|
|
1297
|
+
.blueprint
|
|
1298
|
+
.set('stone.adapter.errorHandlers.default', { module: errorHandler, isClass: true });
|
|
1463
1299
|
context
|
|
1464
1300
|
.modules
|
|
1465
|
-
.filter(module => hasMetadata(module,
|
|
1301
|
+
.filter(module => hasMetadata(module, REACT_ADAPTER_ERROR_PAGE_KEY))
|
|
1466
1302
|
.forEach(module => {
|
|
1467
|
-
const {
|
|
1468
|
-
context.blueprint
|
|
1303
|
+
const { error, layout, adapterAlias, platform } = getMetadata(module, REACT_ADAPTER_ERROR_PAGE_KEY, { error: 'default' });
|
|
1304
|
+
if (isMatchedAdapter(context.blueprint, platform, adapterAlias)) {
|
|
1305
|
+
Array(error).flat().forEach(name => {
|
|
1306
|
+
context
|
|
1307
|
+
.blueprint
|
|
1308
|
+
.set(`stone.useReact.adapterErrorPages.${name}`, { isClass: true, layout, module });
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1469
1311
|
});
|
|
1470
|
-
|
|
1471
|
-
|
|
1312
|
+
// Process both eager and lazy loaded error pages
|
|
1313
|
+
Object
|
|
1314
|
+
.keys(context.blueprint.get('stone.useReact.adapterErrorPages', {}))
|
|
1315
|
+
.forEach((name) => {
|
|
1316
|
+
context
|
|
1317
|
+
.blueprint
|
|
1318
|
+
.set(`stone.adapter.errorHandlers.${name}`, { module: errorHandler, isClass: true });
|
|
1319
|
+
});
|
|
1320
|
+
return context;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1472
1323
|
/**
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
* Set as fallback event handler if none of the other event handlers are registered.
|
|
1476
|
-
*
|
|
1477
|
-
* @param context - The configuration context containing modules and blueprint.
|
|
1478
|
-
* @param next - The next function in the pipeline.
|
|
1479
|
-
* @returns The updated blueprint.
|
|
1324
|
+
* Create an UseReact response.
|
|
1480
1325
|
*
|
|
1481
|
-
* @
|
|
1482
|
-
*
|
|
1483
|
-
* SetUseReactEventHandlerMiddleware({ modules, blueprint }, next);
|
|
1484
|
-
* ```
|
|
1326
|
+
* @param options - The options for creating the response.
|
|
1327
|
+
* @returns The React response.
|
|
1485
1328
|
*/
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
blueprint.set('stone.useReact.componentEventHandler', { module, isComponent: true, isClass: true });
|
|
1329
|
+
const reactResponse = (options) => {
|
|
1330
|
+
if (isNotEmpty(options) &&
|
|
1331
|
+
(isNotEmpty(options.url) ||
|
|
1332
|
+
(isNotEmpty(options.content) && isNotEmpty(options.content.redirect)))) {
|
|
1333
|
+
return reactRedirectResponse(options);
|
|
1492
1334
|
}
|
|
1493
|
-
return
|
|
1494
|
-
}
|
|
1335
|
+
return OutgoingHttpResponse.create(options);
|
|
1336
|
+
};
|
|
1495
1337
|
/**
|
|
1496
|
-
*
|
|
1338
|
+
* Create an UseReact redirect response.
|
|
1497
1339
|
*
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1340
|
+
* @param options - The options for creating the response.
|
|
1341
|
+
* @returns The React redirect response.
|
|
1500
1342
|
*/
|
|
1501
|
-
const
|
|
1502
|
-
{
|
|
1503
|
-
|
|
1504
|
-
{ module: SetSSRCompressionMiddleware, priority: 10 },
|
|
1505
|
-
{ module: SetReactPageLayoutMiddleware, priority: 10 },
|
|
1506
|
-
{ module: SetUseReactEventHandlerMiddleware, priority: 2 },
|
|
1507
|
-
{ module: SetReactKernelErrorPageMiddleware, priority: 10 },
|
|
1508
|
-
{ module: SetReactAdapterErrorPageMiddleware, priority: 10 },
|
|
1509
|
-
{ module: SetReactRouteDefinitionsMiddleware, priority: 10 },
|
|
1510
|
-
{ module: SetBrowserResponseMiddlewareMiddleware, priority: 10 }
|
|
1511
|
-
];
|
|
1343
|
+
const reactRedirectResponse = (options) => {
|
|
1344
|
+
return RedirectResponse.create({ statusCode: 302, ...options });
|
|
1345
|
+
};
|
|
1512
1346
|
|
|
1513
1347
|
/**
|
|
1514
|
-
*
|
|
1348
|
+
* Class representing an UseReactServerErrorHandler.
|
|
1515
1349
|
*
|
|
1516
|
-
*
|
|
1350
|
+
* Adapter level error handler for React applications.
|
|
1517
1351
|
*/
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1352
|
+
class UseReactServerErrorHandler {
|
|
1353
|
+
logger;
|
|
1354
|
+
blueprint;
|
|
1355
|
+
/**
|
|
1356
|
+
* Create an UseReactServerErrorHandler.
|
|
1357
|
+
*
|
|
1358
|
+
* @param options - UseReactServerErrorHandler options.
|
|
1359
|
+
*/
|
|
1360
|
+
constructor({ blueprint }) {
|
|
1361
|
+
this.blueprint = blueprint;
|
|
1362
|
+
this.logger = Logger.getInstance();
|
|
1526
1363
|
}
|
|
1527
|
-
|
|
1364
|
+
/**
|
|
1365
|
+
* Handle an error.
|
|
1366
|
+
*
|
|
1367
|
+
* @param error - The error to handle.
|
|
1368
|
+
* @param context - The context of the adapter.
|
|
1369
|
+
* @returns The raw response.
|
|
1370
|
+
*/
|
|
1371
|
+
async handle(error, context) {
|
|
1372
|
+
this.logger.error(error.message, { error });
|
|
1373
|
+
return context
|
|
1374
|
+
.rawResponseBuilder
|
|
1375
|
+
.add('statusCode', error.statusCode ?? 500)
|
|
1376
|
+
.add('headers', new Headers({ 'Content-Type': 'text/html' }))
|
|
1377
|
+
.add('body', await this.getErrorBody(error, context));
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Get the error body.
|
|
1381
|
+
*
|
|
1382
|
+
* @param error - The error to handle.
|
|
1383
|
+
* @returns The error body.
|
|
1384
|
+
*/
|
|
1385
|
+
async getErrorBody(error, context) {
|
|
1386
|
+
const statusCode = error.statusCode ?? 500;
|
|
1387
|
+
const template = htmlTemplate(this.blueprint);
|
|
1388
|
+
const ClientApp = await buildAdapterErrorComponent(this.blueprint, context, statusCode, error);
|
|
1389
|
+
return template.replace('<!--app-html-->', renderToString(ClientApp));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1528
1392
|
|
|
1529
1393
|
/**
|
|
1530
|
-
*
|
|
1394
|
+
* Blueprint middleware to dynamically set lifecycle hooks for react.
|
|
1531
1395
|
*
|
|
1532
|
-
* @param
|
|
1533
|
-
* @param
|
|
1534
|
-
* @
|
|
1535
|
-
*
|
|
1396
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1397
|
+
* @param next - The next pipeline function to continue processing.
|
|
1398
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1399
|
+
*
|
|
1400
|
+
* @example
|
|
1401
|
+
* ```typescript
|
|
1402
|
+
* SetUseReactHooksMiddleware(context, next)
|
|
1403
|
+
* ```
|
|
1536
1404
|
*/
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
if (isObjectLikeModule(optionsOrBlueprints)) {
|
|
1545
|
-
options = optionsOrBlueprints;
|
|
1546
|
-
blueprints = Array.isArray(maybeBlueprints) ? maybeBlueprints : [];
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
else if (isObjectLikeModule(moduleOrOptions)) { // Pattern: defineStoneReactApp(options, blueprints?)
|
|
1550
|
-
options = moduleOrOptions;
|
|
1551
|
-
blueprints = Array.isArray(optionsOrBlueprints) ? optionsOrBlueprints : [];
|
|
1552
|
-
}
|
|
1553
|
-
const stonePart = {
|
|
1554
|
-
...options,
|
|
1555
|
-
useReact: {
|
|
1556
|
-
...options.useReact
|
|
1557
|
-
}
|
|
1558
|
-
};
|
|
1559
|
-
if (isNotEmpty(module)) {
|
|
1560
|
-
stonePart.useReact.componentEventHandler = {
|
|
1561
|
-
module,
|
|
1562
|
-
isComponent: true,
|
|
1563
|
-
isClass: options.isClass,
|
|
1564
|
-
isFactory: options.isClass !== true
|
|
1565
|
-
};
|
|
1405
|
+
const SetUseReactHooksMiddleware = (context, next) => {
|
|
1406
|
+
const currentPlatform = context.blueprint.get('stone.adapter.platform', '');
|
|
1407
|
+
const ignorePlatforms = context.blueprint.get('stone.useReact.ignorePlatforms', []);
|
|
1408
|
+
if (!ignorePlatforms.includes(currentPlatform)) {
|
|
1409
|
+
context
|
|
1410
|
+
.blueprint
|
|
1411
|
+
.add('stone.lifecycleHooks.onPreparingResponse', [onPreparingResponse]);
|
|
1566
1412
|
}
|
|
1567
|
-
return
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1413
|
+
return next(context);
|
|
1414
|
+
};
|
|
1570
1415
|
/**
|
|
1571
|
-
*
|
|
1416
|
+
* Blueprint middleware to process and register kernel error page definitions from modules.
|
|
1572
1417
|
*
|
|
1573
|
-
* @param
|
|
1574
|
-
* @
|
|
1418
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1419
|
+
* @param next - The next pipeline function to continue processing.
|
|
1420
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1575
1421
|
*
|
|
1576
1422
|
* @example
|
|
1577
1423
|
* ```typescript
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
* @AdapterErrorPage({ error: 'UserNotFoundError' })
|
|
1581
|
-
* class UserAdapterErrorPage {
|
|
1582
|
-
* render({ error }) {
|
|
1583
|
-
* return <h1>User name: {error.message}</h1>;
|
|
1584
|
-
* }
|
|
1585
|
-
* }
|
|
1424
|
+
* SetReactKernelErrorPageMiddleware(context, next)
|
|
1586
1425
|
* ```
|
|
1587
1426
|
*/
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
|
|
1427
|
+
const SetReactKernelErrorPageMiddleware = (context, next) => {
|
|
1428
|
+
context
|
|
1429
|
+
.blueprint
|
|
1430
|
+
.set('stone.kernel.errorHandlers.default', { module: UseReactKernelErrorHandler, isClass: true });
|
|
1431
|
+
context
|
|
1432
|
+
.modules
|
|
1433
|
+
.filter(module => hasMetadata(module, REACT_ERROR_PAGE_KEY))
|
|
1434
|
+
.forEach(module => {
|
|
1435
|
+
const { error, layout } = getMetadata(module, REACT_ERROR_PAGE_KEY, { error: 'default' });
|
|
1436
|
+
Array(error).flat().forEach(name => {
|
|
1437
|
+
context
|
|
1438
|
+
.blueprint
|
|
1439
|
+
.set(`stone.useReact.errorPages.${name}`, { layout, module, isClass: true });
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1442
|
+
// Process both eager and lazy loaded error pages
|
|
1443
|
+
Object
|
|
1444
|
+
.keys(context.blueprint.get('stone.useReact.errorPages', {}))
|
|
1445
|
+
.forEach((name) => {
|
|
1446
|
+
context
|
|
1447
|
+
.blueprint
|
|
1448
|
+
.set(`stone.kernel.errorHandlers.${name}`, { module: UseReactKernelErrorHandler, isClass: true });
|
|
1591
1449
|
});
|
|
1450
|
+
return next(context);
|
|
1592
1451
|
};
|
|
1593
|
-
|
|
1594
1452
|
/**
|
|
1595
|
-
*
|
|
1453
|
+
* Blueprint middleware to process and register route definitions from modules.
|
|
1596
1454
|
*
|
|
1597
|
-
* @param
|
|
1598
|
-
* @
|
|
1455
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1456
|
+
* @param next - The next pipeline function to continue processing.
|
|
1457
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1599
1458
|
*
|
|
1600
1459
|
* @example
|
|
1601
1460
|
* ```typescript
|
|
1602
|
-
*
|
|
1603
|
-
*
|
|
1604
|
-
* @ErrorPage({ error: 'UserNotFoundError' })
|
|
1605
|
-
* class UserErrorPage {
|
|
1606
|
-
* render({ error }) {
|
|
1607
|
-
* return <h1>User name: {error.message}</h1>;
|
|
1608
|
-
* }
|
|
1609
|
-
* }
|
|
1461
|
+
* SetReactRouteDefinitionsMiddleware(context, next)
|
|
1610
1462
|
* ```
|
|
1611
1463
|
*/
|
|
1612
|
-
const
|
|
1613
|
-
|
|
1614
|
-
|
|
1464
|
+
const SetReactRouteDefinitionsMiddleware = (context, next) => {
|
|
1465
|
+
context
|
|
1466
|
+
.modules
|
|
1467
|
+
.filter(module => hasMetadata(module, REACT_PAGE_KEY))
|
|
1468
|
+
.forEach(module => {
|
|
1469
|
+
const options = getMetadata(module, REACT_PAGE_KEY, { path: '/' });
|
|
1470
|
+
const definition = {
|
|
1471
|
+
...options,
|
|
1472
|
+
handler: { ...options.handler, module }
|
|
1473
|
+
};
|
|
1474
|
+
context.blueprint.add('stone.router.definitions', [definition]);
|
|
1615
1475
|
});
|
|
1476
|
+
return next(context);
|
|
1616
1477
|
};
|
|
1617
|
-
|
|
1618
1478
|
/**
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1479
|
+
* Blueprint middleware to process and register layout definitions from modules.
|
|
1480
|
+
*
|
|
1481
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1482
|
+
* @param next - The next pipeline function to continue processing.
|
|
1483
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1621
1484
|
*
|
|
1622
1485
|
* @example
|
|
1623
1486
|
* ```typescript
|
|
1624
|
-
*
|
|
1625
|
-
* // ...
|
|
1626
|
-
* @Hook('onPreparingPage')
|
|
1627
|
-
* onPreparingPage () {}
|
|
1628
|
-
* }
|
|
1487
|
+
* SetReactPageLayoutMiddleware(context, next)
|
|
1629
1488
|
* ```
|
|
1630
|
-
*
|
|
1631
|
-
* @param name - The name of the lifecycle hook.
|
|
1632
|
-
* @returns A class decorator function that sets the metadata using the provided options.
|
|
1633
1489
|
*/
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1490
|
+
const SetReactPageLayoutMiddleware = (context, next) => {
|
|
1491
|
+
context
|
|
1492
|
+
.modules
|
|
1493
|
+
.filter(module => hasMetadata(module, REACT_PAGE_LAYOUT_KEY))
|
|
1494
|
+
.forEach(module => {
|
|
1495
|
+
const { name = 'default' } = getMetadata(module, REACT_PAGE_LAYOUT_KEY, { name: 'default' });
|
|
1496
|
+
context.blueprint.set(`stone.useReact.layout.${name}`, { isClass: true, module });
|
|
1637
1497
|
});
|
|
1498
|
+
return next(context);
|
|
1638
1499
|
};
|
|
1639
|
-
|
|
1640
1500
|
/**
|
|
1641
|
-
*
|
|
1642
|
-
* Uses the `Match` decorator internally to register the route with the HTTP `GET` method.
|
|
1501
|
+
* Blueprint middleware to set the UseReact as the main event handler for the application.
|
|
1643
1502
|
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1503
|
+
* Set as fallback event handler if none of the other event handlers are registered.
|
|
1504
|
+
*
|
|
1505
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1506
|
+
* @param next - The next function in the pipeline.
|
|
1507
|
+
* @returns The updated blueprint.
|
|
1646
1508
|
*
|
|
1647
1509
|
* @example
|
|
1648
1510
|
* ```typescript
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1651
|
-
* @Page('/user-profile')
|
|
1652
|
-
* class UserPage {
|
|
1653
|
-
* handle({ event }): Record<string, string> {
|
|
1654
|
-
* return { name: 'Jane Doe' };
|
|
1655
|
-
* }
|
|
1656
|
-
*
|
|
1657
|
-
* render({ data }) {
|
|
1658
|
-
* return <h1>User name: {data.name}</h1>;
|
|
1659
|
-
* }
|
|
1660
|
-
* }
|
|
1511
|
+
* SetUseReactEventHandlerMiddleware({ modules, blueprint }, next);
|
|
1661
1512
|
* ```
|
|
1662
1513
|
*/
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
});
|
|
1673
|
-
};
|
|
1514
|
+
async function SetUseReactEventHandlerMiddleware(context, next) {
|
|
1515
|
+
const blueprint = await next(context);
|
|
1516
|
+
const module = context.modules.find(module => hasMetadata(module, STONE_REACT_APP_KEY));
|
|
1517
|
+
blueprint.setIf('stone.kernel.eventHandler', { module: UseReactEventHandler, isClass: true });
|
|
1518
|
+
if (isNotEmpty(module)) {
|
|
1519
|
+
blueprint.set('stone.useReact.componentEventHandler', { module, isComponent: true, isClass: true });
|
|
1520
|
+
}
|
|
1521
|
+
return blueprint;
|
|
1522
|
+
}
|
|
1674
1523
|
|
|
1675
1524
|
/**
|
|
1676
|
-
*
|
|
1525
|
+
* Blueprint middleware to process and register adapter error page definitions from modules.
|
|
1677
1526
|
*
|
|
1678
|
-
* @param
|
|
1679
|
-
* @
|
|
1527
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1528
|
+
* @param next - The next pipeline function to continue processing.
|
|
1529
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1680
1530
|
*
|
|
1681
1531
|
* @example
|
|
1682
1532
|
* ```typescript
|
|
1683
|
-
*
|
|
1684
|
-
*
|
|
1685
|
-
* @PageLayout({ name: 'UserPageLayout' })
|
|
1686
|
-
* class UserPageLayout {
|
|
1687
|
-
* render({ data }) {
|
|
1688
|
-
* return <h1>User name: {data.name}</h1>;
|
|
1689
|
-
* }
|
|
1690
|
-
* }
|
|
1533
|
+
* SetReactAdapterErrorPageMiddleware(context, next)
|
|
1691
1534
|
* ```
|
|
1692
1535
|
*/
|
|
1693
|
-
const
|
|
1694
|
-
return
|
|
1695
|
-
setMetadata(context, REACT_PAGE_LAYOUT_KEY, { ...options, isClass: true });
|
|
1696
|
-
});
|
|
1536
|
+
const SetReactAdapterErrorPageMiddleware = (context, next) => {
|
|
1537
|
+
return next(setUseReactAdapterErrorHandler(UseReactServerErrorHandler, context));
|
|
1697
1538
|
};
|
|
1698
|
-
|
|
1699
1539
|
/**
|
|
1700
|
-
*
|
|
1540
|
+
* Blueprint middleware to set StaticFileMiddleware for SSR adapter.
|
|
1701
1541
|
*
|
|
1702
|
-
* @param
|
|
1703
|
-
* @param
|
|
1704
|
-
* @returns
|
|
1542
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1543
|
+
* @param next - The next pipeline function to continue processing.
|
|
1544
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1705
1545
|
*
|
|
1706
1546
|
* @example
|
|
1707
1547
|
* ```typescript
|
|
1708
|
-
*
|
|
1709
|
-
*
|
|
1710
|
-
* @Page('/user-profile')
|
|
1711
|
-
* class UserPage {
|
|
1712
|
-
* @PageStatus()
|
|
1713
|
-
* handle() {
|
|
1714
|
-
* return { name: 'John Doe' };
|
|
1715
|
-
* }
|
|
1716
|
-
* }
|
|
1548
|
+
* SetSSRStaticFileMiddleware(context, next)
|
|
1717
1549
|
* ```
|
|
1718
1550
|
*/
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
const content = await target.apply(this, args);
|
|
1723
|
-
return { content, statusCode, headers };
|
|
1724
|
-
};
|
|
1725
|
-
});
|
|
1551
|
+
const SetSSRStaticFileMiddleware = async (context, next) => {
|
|
1552
|
+
context.blueprint.add('stone.kernel.middleware', [MetaStaticFileMiddleware]);
|
|
1553
|
+
return await next(context);
|
|
1726
1554
|
};
|
|
1727
|
-
|
|
1728
1555
|
/**
|
|
1729
|
-
*
|
|
1556
|
+
* Blueprint middleware to set CompressionMiddleware for SSR adapter.
|
|
1730
1557
|
*
|
|
1731
|
-
* @param
|
|
1732
|
-
* @
|
|
1558
|
+
* @param context - The configuration context containing modules and blueprint.
|
|
1559
|
+
* @param next - The next pipeline function to continue processing.
|
|
1560
|
+
* @returns The updated blueprint or a promise resolving to it.
|
|
1733
1561
|
*
|
|
1734
1562
|
* @example
|
|
1735
1563
|
* ```typescript
|
|
1736
|
-
*
|
|
1737
|
-
* import { Snapshot } from '@stone-js/use-react';
|
|
1738
|
-
*
|
|
1739
|
-
* @Service({ alias: 'userService' })
|
|
1740
|
-
* class UserService {
|
|
1741
|
-
* @Snapshot()
|
|
1742
|
-
* showProfile() {
|
|
1743
|
-
* return { name: 'John Doe' };
|
|
1744
|
-
* }
|
|
1745
|
-
* }
|
|
1564
|
+
* SetSSRCompressionMiddleware(context, next)
|
|
1746
1565
|
* ```
|
|
1747
1566
|
*/
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
name = name ?? `${String(Object.getPrototypeOf(this).constructor.name)}.${String(context.name)}`;
|
|
1752
|
-
return await ReactRuntime.instance?.snapshot(name, () => target.apply(this, args));
|
|
1753
|
-
};
|
|
1754
|
-
});
|
|
1567
|
+
const SetSSRCompressionMiddleware = async (context, next) => {
|
|
1568
|
+
context.blueprint.add('stone.kernel.middleware', [MetaCompressionMiddleware]);
|
|
1569
|
+
return await next(context);
|
|
1755
1570
|
};
|
|
1571
|
+
/**
|
|
1572
|
+
* Configuration for react processing middleware.
|
|
1573
|
+
*
|
|
1574
|
+
* This array defines a list of middleware pipes, each with a `pipe` function and a `priority`.
|
|
1575
|
+
* These pipes are executed in the order of their priority values, with lower values running first.
|
|
1576
|
+
*/
|
|
1577
|
+
const metaServerUseReactBlueprintMiddleware = [
|
|
1578
|
+
{ module: SetSSRStaticFileMiddleware, priority: 10 },
|
|
1579
|
+
{ module: SetUseReactHooksMiddleware, priority: 10 },
|
|
1580
|
+
{ module: SetSSRCompressionMiddleware, priority: 10 },
|
|
1581
|
+
{ module: SetReactPageLayoutMiddleware, priority: 10 },
|
|
1582
|
+
{ module: SetUseReactEventHandlerMiddleware, priority: 2 },
|
|
1583
|
+
{ module: SetReactKernelErrorPageMiddleware, priority: 10 },
|
|
1584
|
+
{ module: SetReactAdapterErrorPageMiddleware, priority: 10 },
|
|
1585
|
+
{ module: SetReactRouteDefinitionsMiddleware, priority: 10 }
|
|
1586
|
+
];
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Middleware for the React blueprint.
|
|
1590
|
+
*/
|
|
1591
|
+
internalUseReactBlueprint.stone.useReact.ignorePlatforms = [NODE_CONSOLE_PLATFORM];
|
|
1592
|
+
internalUseReactBlueprint.stone.blueprint = { middleware: metaServerUseReactBlueprintMiddleware };
|
|
1593
|
+
/**
|
|
1594
|
+
* Default blueprint for a React-based Stone.js application.
|
|
1595
|
+
*
|
|
1596
|
+
* - Defines middleware, lifecycle hooks, and the default HTML template path.
|
|
1597
|
+
*/
|
|
1598
|
+
const useReactBlueprint = internalUseReactBlueprint;
|
|
1756
1599
|
|
|
1757
1600
|
/**
|
|
1758
1601
|
* UseReact decorator.
|
|
@@ -1784,16 +1627,26 @@ const StoneClient = ({ children }) => {
|
|
|
1784
1627
|
/**
|
|
1785
1628
|
* Internal link component using Stone.js router.
|
|
1786
1629
|
*/
|
|
1787
|
-
const
|
|
1630
|
+
const StoneLink = ({ to, href, noRel, external, children, ariaCurrentValue = 'page', selectedClass = 'selected', ...rest }) => {
|
|
1631
|
+
const isExternal = external === true;
|
|
1632
|
+
const shouldHandleNav = !isExternal && isNotEmpty(to);
|
|
1788
1633
|
const router = useContext(StoneContext).container.resolve(Router);
|
|
1789
|
-
const path =
|
|
1634
|
+
const path = useMemo(() => {
|
|
1635
|
+
return isObjectLikeModule(to) ? router.generate(to) : to ?? href ?? '#';
|
|
1636
|
+
}, [to, href, router]);
|
|
1790
1637
|
const [currentRoute, setCurrentRoute] = useState(router.getCurrentRoute());
|
|
1791
1638
|
const selectedClassName = currentRoute?.path === path ? selectedClass : undefined;
|
|
1792
|
-
const elemClassName = [className, selectedClassName].filter(Boolean).join(' ').trim();
|
|
1639
|
+
const elemClassName = [rest.className, selectedClassName].filter(Boolean).join(' ').trim();
|
|
1793
1640
|
const handleClick = (event) => {
|
|
1641
|
+
rest.onClick?.(event);
|
|
1642
|
+
if (event.defaultPrevented || isExternal)
|
|
1643
|
+
return;
|
|
1794
1644
|
event.preventDefault();
|
|
1795
|
-
router.navigate(to
|
|
1645
|
+
isNotEmpty(to) && router.navigate(to);
|
|
1796
1646
|
};
|
|
1647
|
+
if (isEmpty(to) && isEmpty(href)) {
|
|
1648
|
+
Logger.warn('StoneLink: missing "to" or "href"');
|
|
1649
|
+
}
|
|
1797
1650
|
useEffect(() => {
|
|
1798
1651
|
const routerEventHandler = (event) => {
|
|
1799
1652
|
setCurrentRoute(event.get('route'));
|
|
@@ -1803,19 +1656,13 @@ const InternalLink = ({ to, href, noRel, children, className, defaultNav, select
|
|
|
1803
1656
|
router.off(RouteEvent.ROUTED, routerEventHandler);
|
|
1804
1657
|
};
|
|
1805
1658
|
}, [router]);
|
|
1806
|
-
return
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
const ExternalLink = ({ to, href, noRel, target, children, className, ariaCurrentValue = 'page', rel = 'noopener noreferrer' }) => (jsx("a", { target: target, className: className, "aria-current": ariaCurrentValue, rel: noRel !== undefined ? undefined : rel, href: typeof to === 'string' ? to : href, children: children }));
|
|
1814
|
-
/**
|
|
1815
|
-
* Main StoneLink component delegating to internal or external versions.
|
|
1816
|
-
*/
|
|
1817
|
-
const StoneLink = (props) => {
|
|
1818
|
-
return props.external === true ? jsx(ExternalLink, { ...props }) : jsx(InternalLink, { ...props });
|
|
1659
|
+
return (
|
|
1660
|
+
// eslint-disable-next-line react/jsx-no-target-blank
|
|
1661
|
+
jsx("a", { ...rest, href: path, className: elemClassName, target: isExternal ? '_blank' : rest.target, "aria-current": isNotEmpty(selectedClassName) ? ariaCurrentValue : undefined, rel: noRel === true
|
|
1662
|
+
? undefined
|
|
1663
|
+
: isExternal
|
|
1664
|
+
? 'noopener noreferrer'
|
|
1665
|
+
: rest.rel, onClick: shouldHandleNav ? handleClick : rest.onClick, children: children }));
|
|
1819
1666
|
};
|
|
1820
1667
|
|
|
1821
1668
|
/**
|
|
@@ -1830,7 +1677,7 @@ const StoneLink = (props) => {
|
|
|
1830
1677
|
* @param options - The options to create the Stone Outlet.
|
|
1831
1678
|
* @returns The Stone Outlet component.
|
|
1832
1679
|
*/
|
|
1833
|
-
const StoneOutlet = ({ children }) => {
|
|
1680
|
+
const StoneOutlet = ({ children, ...rest }) => {
|
|
1834
1681
|
const [currentView, setCurrentView] = useState(children);
|
|
1835
1682
|
useEffect(() => {
|
|
1836
1683
|
const eventName = STONE_PAGE_EVENT_OUTLET;
|
|
@@ -1842,7 +1689,7 @@ const StoneOutlet = ({ children }) => {
|
|
|
1842
1689
|
window.addEventListener(eventName, handleEvent);
|
|
1843
1690
|
return () => window.removeEventListener(eventName, handleEvent);
|
|
1844
1691
|
}, []);
|
|
1845
|
-
return jsx("div", { "data-stone-outlet": 'true', children: currentView });
|
|
1692
|
+
return jsx("div", { ...rest, "data-stone-outlet": 'true', children: currentView });
|
|
1846
1693
|
};
|
|
1847
1694
|
|
|
1848
1695
|
/**
|
|
@@ -1856,4 +1703,4 @@ const StoneServer = ({ children }) => {
|
|
|
1856
1703
|
return isServer() ? jsx(Fragment, { children: children }) : jsx(Fragment, {});
|
|
1857
1704
|
};
|
|
1858
1705
|
|
|
1859
|
-
export { AdapterErrorPage,
|
|
1706
|
+
export { AdapterErrorPage, ErrorPage, Hook, MetaReactRuntime, MetaUseReactServiceProvider, Page, PageLayout, PageStatus, REACT_ADAPTER_ERROR_PAGE_KEY, REACT_ERROR_PAGE_KEY, REACT_PAGE_KEY, REACT_PAGE_LAYOUT_KEY, ReactRuntime, STONE_DOM_ATTR, STONE_PAGE_EVENT_OUTLET, STONE_REACT_APP_KEY, STONE_SNAPSHOT, SetReactAdapterErrorPageMiddleware, SetReactKernelErrorPageMiddleware, SetReactPageLayoutMiddleware, SetReactRouteDefinitionsMiddleware, SetSSRCompressionMiddleware, SetSSRStaticFileMiddleware, SetUseReactEventHandlerMiddleware, SetUseReactHooksMiddleware, Snapshot, StoneClient, StoneContext, StoneError, StoneLink, StoneOutlet, StonePage, StoneServer, UseReact, UseReactError, UseReactEventHandler, UseReactKernelErrorHandler, UseReactServerErrorHandler, UseReactServiceProvider, applyHeadContextToDom, applyHeadContextToHtmlString, applyMeta, buildAdapterErrorComponent, buildAppComponent, buildLayoutComponent, buildPageComponent, defineAdapterErrorPage, defineErrorPage, definePage, definePageLayout, defineStoneReactApp, executeHandler, executeHooks, getAppRootElement, getBrowserContent, getResponseSnapshot, getServerContent, htmlTemplate, hydrateReactApp, internalUseReactBlueprint, isClient, isSSR, isServer, metaServerUseReactBlueprintMiddleware, onPreparingResponse, prepareErrorPage, prepareFallbackErrorPage, preparePage, reactRedirectResponse, reactResponse, renderReactApp, renderStoneSnapshot, resolveComponent, resolveLazyComponent, setUseReactAdapterErrorHandler, snapshotResponse, useReactBlueprint };
|