@common-stack/generate-plugin 5.0.2-alpha.4

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.
Files changed (49) hide show
  1. package/.eslintrc.json +32 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +21 -0
  4. package/README.md +11 -0
  5. package/generators.json +9 -0
  6. package/jest.config.ts +10 -0
  7. package/lib/generators/add-entries/entries/antui/entry.client.tsx.template +100 -0
  8. package/lib/generators/add-entries/entries/antui/entry.server.tsx.template +254 -0
  9. package/lib/generators/add-entries/entries/antui/root.tsx.template +107 -0
  10. package/lib/generators/add-entries/entries/chakraui/context.tsx.template +20 -0
  11. package/lib/generators/add-entries/entries/chakraui/entry.client.tsx.template +105 -0
  12. package/lib/generators/add-entries/entries/chakraui/entry.server.tsx.template +256 -0
  13. package/lib/generators/add-entries/entries/chakraui/root.tsx.template +97 -0
  14. package/lib/generators/add-entries/files/common/AntStyles.tsx.template +8 -0
  15. package/lib/generators/add-entries/files/common/createEmotionCache.ts.template +7 -0
  16. package/lib/generators/add-entries/files/common/index.ts.template +3 -0
  17. package/lib/generators/add-entries/files/common/utils.ts.template +16 -0
  18. package/lib/generators/add-entries/generator.cjs +55 -0
  19. package/lib/generators/add-entries/generator.cjs.map +1 -0
  20. package/lib/generators/add-entries/generator.d.ts +4 -0
  21. package/lib/generators/add-entries/generator.mjs +55 -0
  22. package/lib/generators/add-entries/generator.mjs.map +1 -0
  23. package/lib/generators/add-entries/generator.spec.d.ts +1 -0
  24. package/lib/generators/add-entries/schema.json +13 -0
  25. package/lib/index.cjs +1 -0
  26. package/lib/index.cjs.map +1 -0
  27. package/lib/index.d.ts +1 -0
  28. package/lib/index.mjs +1 -0
  29. package/lib/index.mjs.map +1 -0
  30. package/package.json +28 -0
  31. package/project.json +7 -0
  32. package/rollup.config.mjs +65 -0
  33. package/src/generators/add-entries/entries/antui/entry.client.tsx.template +100 -0
  34. package/src/generators/add-entries/entries/antui/entry.server.tsx.template +254 -0
  35. package/src/generators/add-entries/entries/antui/root.tsx.template +107 -0
  36. package/src/generators/add-entries/entries/chakraui/context.tsx.template +20 -0
  37. package/src/generators/add-entries/entries/chakraui/entry.client.tsx.template +105 -0
  38. package/src/generators/add-entries/entries/chakraui/entry.server.tsx.template +256 -0
  39. package/src/generators/add-entries/entries/chakraui/root.tsx.template +97 -0
  40. package/src/generators/add-entries/files/common/AntStyles.tsx.template +8 -0
  41. package/src/generators/add-entries/files/common/createEmotionCache.ts.template +7 -0
  42. package/src/generators/add-entries/files/common/index.ts.template +3 -0
  43. package/src/generators/add-entries/files/common/utils.ts.template +16 -0
  44. package/src/generators/add-entries/generator.spec.ts +20 -0
  45. package/src/generators/add-entries/generator.ts +66 -0
  46. package/src/generators/add-entries/schema.d.ts +3 -0
  47. package/src/generators/add-entries/schema.json +13 -0
  48. package/src/index.ts +2 -0
  49. package/tsconfig.json +26 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "extends": ["../../.eslintrc.js"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {}
8
+ },
9
+ {
10
+ "files": ["*.ts", "*.tsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.js", "*.jsx"],
15
+ "rules": {}
16
+ },
17
+ {
18
+ "files": ["*.json"],
19
+ "parser": "jsonc-eslint-parser",
20
+ "rules": {
21
+ "@nx/dependency-checks": "error"
22
+ }
23
+ },
24
+ {
25
+ "files": ["./package.json", "./generators.json"],
26
+ "parser": "jsonc-eslint-parser",
27
+ "rules": {
28
+ "@nx/nx-plugin-checks": "error"
29
+ }
30
+ }
31
+ ]
32
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## [5.0.2-alpha.4](https://github.com/cdmbase/common-stack/compare/v5.0.2-alpha.3...v5.0.2-alpha.4) (2024-07-23)
7
+
8
+ **Note:** Version bump only for package @common-stack/generate-plugin
9
+
10
+ ## [5.0.2-alpha.3](https://github.com/cdmbase/common-stack/compare/v5.0.2-alpha.2...v5.0.2-alpha.3) (2024-07-23)
11
+
12
+ **Note:** Version bump only for package @common-stack/generate-plugin
13
+
14
+ ## [5.0.2-alpha.2](https://github.com/cdmbase/common-stack/compare/v5.0.2-alpha.1...v5.0.2-alpha.2) (2024-07-20)
15
+
16
+ **Note:** Version bump only for package modal-ui
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2017 CDMBase LLC.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # generate-plugin
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build generate-plugin` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test generate-plugin` to execute the unit tests via [Jest](https://jestjs.io).
@@ -0,0 +1,9 @@
1
+ {
2
+ "generators": {
3
+ "add-entries": {
4
+ "factory": "./lib/generators/add-entries/generator.cjs",
5
+ "schema": "./lib/generators/add-entries/schema.json",
6
+ "description": "add-entries generator"
7
+ }
8
+ }
9
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ displayName: '@common-stack/generate-plugin',
4
+ preset: '../../jest.preset.js',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../coverage/@common-stack/generate-plugin',
10
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * By default, Remix will handle hydrating your app on the client for you.
3
+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4
+ * For more information, see https://remix.run/file-conventions/entry.client
5
+ */
6
+ import * as React from 'react';
7
+ import 'reflect-metadata';
8
+ import { RemixBrowser } from '@remix-run/react';
9
+ import { startTransition, StrictMode } from 'react';
10
+ import { hydrateRoot } from 'react-dom/client';
11
+ import { createCache, StyleProvider } from '@ant-design/cssinjs';
12
+ import { ApolloProvider } from '@apollo/client/index.js';
13
+ import { SlotFillProvider, removeUniversalPortals } from '@common-stack/components-pro';
14
+ import { InversifyProvider } from '@common-stack/client-react';
15
+ import { Provider as ReduxProvider } from 'react-redux';
16
+ import { PersistGate } from 'redux-persist/integration/react';
17
+ import { persistStore } from 'redux-persist';
18
+ import { CacheProvider } from '@emotion/react';
19
+ import i18next from 'i18next';
20
+ import { I18nextProvider, initReactI18next } from 'react-i18next';
21
+ import LanguageDetector from 'i18next-browser-languagedetector';
22
+ import Backend from 'i18next-http-backend';
23
+ // @ts-ignore
24
+ import { getInitialNamespaces } from 'remix-i18next/client';
25
+ // @ts-ignore
26
+ import { createReduxStore } from '@app/frontend-stack-react/config/redux-config.js';
27
+ // @ts-ignore
28
+ import { createClientContainer } from '@app/frontend-stack-react/config/client.service';
29
+ // @ts-ignore
30
+ import clientModules from '@app/frontend-stack-react/modules.js';
31
+ // @ts-ignore
32
+ import createEmotionCache from '@app/frontend-stack-react/entries/common/createEmotionCache.js';
33
+ // @ts-ignore
34
+ import config from '@app/cde-webconfig.json';
35
+
36
+ const { apolloClient: client, container, serviceFunc } = createClientContainer();
37
+ const { store } = createReduxStore(client, serviceFunc(), container);
38
+ const persistor = persistStore(store);
39
+ const antCache = createCache();
40
+ const cache = createEmotionCache();
41
+
42
+ (window as any).__remixStore = store;
43
+ removeUniversalPortals((window as any).__SLOT_FILLS__ || []);
44
+
45
+ async function hydrate() {
46
+ if (!i18next.isInitialized && config.i18n.enabled) {
47
+ await i18next
48
+ .use(initReactI18next)
49
+ .use(LanguageDetector)
50
+ .use(Backend)
51
+ .init({
52
+ fallbackLng: config.i18n.fallbackLng,
53
+ defaultNS: config.i18n.defaultNS,
54
+ react: config.i18n.react,
55
+ supportedLngs: config.i18n.supportedLngs,
56
+ backend: config.i18n.backend,
57
+ ns: getInitialNamespaces(),
58
+ detection: {
59
+ order: ['htmlTag'],
60
+ caches: [],
61
+ },
62
+ });
63
+ }
64
+ startTransition(() => {
65
+ hydrateRoot(
66
+ document,
67
+ (
68
+ <I18nextProvider i18n={i18next}>
69
+ <StrictMode>
70
+ <CacheProvider value={cache}>
71
+ <StyleProvider cache={antCache}>
72
+ <ReduxProvider store={store}>
73
+ <SlotFillProvider>
74
+ <InversifyProvider container={container} modules={clientModules}>
75
+ <PersistGate loading={null} persistor={persistor}>
76
+ {() => (
77
+ <ApolloProvider client={client}>
78
+ <RemixBrowser />
79
+ </ApolloProvider>
80
+ )}
81
+ </PersistGate>
82
+ </InversifyProvider>
83
+ </SlotFillProvider>
84
+ </ReduxProvider>
85
+ </StyleProvider>
86
+ </CacheProvider>
87
+ </StrictMode>
88
+ </I18nextProvider>
89
+ ) as any,
90
+ );
91
+ });
92
+ }
93
+
94
+ if (window.requestIdleCallback) {
95
+ window.requestIdleCallback(hydrate);
96
+ } else {
97
+ // Safari doesn't support requestIdleCallback
98
+ // https://caniuse.com/requestidlecallback
99
+ window.setTimeout(hydrate, 1);
100
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * By default, Remix will handle generating the HTTP Response for you.
3
+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4
+ * For more information, see https://remix.run/file-conventions/entry.server
5
+ */
6
+ import * as React from 'react';
7
+ (global as any).__CLIENT__ = false;
8
+ (global as any).__SERVER__ = true;
9
+ import { PassThrough, Transform } from 'node:stream';
10
+ import type { EntryContext } from '@remix-run/node';
11
+ import { createReadableStreamFromReadable } from '@remix-run/node';
12
+ import { RemixServer } from '@remix-run/react';
13
+ import { isbot } from 'isbot';
14
+ import { ApolloProvider } from '@apollo/client/index.js';
15
+ import { SlotFillProvider } from '@common-stack/components-pro';
16
+ import { InversifyProvider } from '@common-stack/client-react';
17
+ import { renderToPipeableStream } from 'react-dom/server';
18
+ import { Provider as ReduxProvider } from 'react-redux';
19
+ import { LOCATION_CHANGE } from '@common-stack/remix-router-redux';
20
+ import serialize from 'serialize-javascript';
21
+ import { createCache as createAntdCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
22
+ import { CacheProvider } from '@emotion/react';
23
+ import { renderStylesToNodeStream } from '@emotion/server';
24
+ import { createInstance } from 'i18next';
25
+ import { I18nextProvider, initReactI18next } from 'react-i18next';
26
+ import Backend from 'i18next-fs-backend';
27
+ import { resolve } from 'node:path';
28
+ // @ts-ignore
29
+ import { i18nextInstance as i18next } from '@app/frontend-stack-react/i18n-localization/i18next.server.js';
30
+ import config from '@app/cde-webconfig.json';
31
+ // @ts-ignore
32
+ import createEmotionCache from '@app/frontend-stack-react/entries/common/createEmotionCache';
33
+ import type { IAppLoadContext } from '@common-stack/client-core';
34
+
35
+ const ABORT_DELAY = 5_000;
36
+ const antdCache = createAntdCache();
37
+ const cache = createEmotionCache();
38
+
39
+ class ConstantsTransform extends Transform {
40
+ _fills: string[];
41
+ _apolloState: any;
42
+ _reduxState: any;
43
+ _styleSheet: string;
44
+
45
+ constructor(fills: string[], apolloState: any, reduxState: any, styleSheet: any) {
46
+ super();
47
+ this._fills = fills;
48
+ this._apolloState = apolloState;
49
+ this._reduxState = reduxState;
50
+ this._styleSheet = styleSheet;
51
+ }
52
+
53
+ _transform(chunk, encoding, callback) {
54
+ let transformedChunk = chunk.toString();
55
+
56
+ if (transformedChunk.includes('[__APOLLO_STATE__]')) {
57
+ transformedChunk = transformedChunk.replace(
58
+ '[__APOLLO_STATE__]',
59
+ serialize(this._apolloState, { isJSON: true }),
60
+ );
61
+ }
62
+ if (transformedChunk.includes('[__PRELOADED_STATE__]')) {
63
+ transformedChunk = transformedChunk.replace(
64
+ '[__PRELOADED_STATE__]',
65
+ serialize(this._reduxState, { isJSON: true }),
66
+ );
67
+ }
68
+ if (transformedChunk.includes('[__SLOT_FILLS__]')) {
69
+ transformedChunk = transformedChunk.replace('[__SLOT_FILLS__]', serialize(this._fills, { isJSON: true }));
70
+ }
71
+ if (transformedChunk.includes('[__STYLESHEET__]')) {
72
+ transformedChunk = transformedChunk.replace('[__STYLESHEET__]', this._styleSheet);
73
+ }
74
+
75
+ callback(null, transformedChunk);
76
+ }
77
+ }
78
+
79
+ export default async function handleRequest(
80
+ request: Request,
81
+ responseStatusCode: number,
82
+ responseHeaders: Headers,
83
+ remixContext: EntryContext,
84
+ // This is ignored so we can keep it in the template for visibility. Feel
85
+ // free to delete this parameter in your app if you're not using it!
86
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
87
+ loadContext: IAppLoadContext,
88
+ ) {
89
+ const instance = createInstance();
90
+ const lng = await i18next.getLocale(request);
91
+ const ns = i18next.getRouteNamespaces(remixContext);
92
+
93
+ // First, we create a new instance of i18next so every request will have a
94
+ // completely unique instance and not share any state.
95
+ if (config.i18n.enabled) {
96
+ await instance
97
+ .use(initReactI18next) // Tell our instance to use react-i18next
98
+ .use(Backend) // Setup our backend.init({
99
+ .init({
100
+ fallbackLng: config.i18n.fallbackLng,
101
+ defaultNS: config.i18n.defaultNS,
102
+ react: config.i18n.react,
103
+ supportedLngs: config.i18n.supportedLngs,
104
+ lng, // The locale we detected above
105
+ ns, // The namespaces the routes about to render want to use
106
+ backend: {
107
+ loadPath: resolve(config.i18n.backend.loadServerPath),
108
+ },
109
+ });
110
+ }
111
+
112
+ return isbot(request.headers.get('user-agent') || '')
113
+ ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext, loadContext, instance)
114
+ : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext, loadContext, instance);
115
+ }
116
+
117
+ function handleBotRequest(
118
+ request: Request,
119
+ responseStatusCode: number,
120
+ responseHeaders: Headers,
121
+ remixContext: EntryContext,
122
+ loadContext: IAppLoadContext,
123
+ i18nInstance: i18next,
124
+ ) {
125
+ return new Promise((resolve, reject) => {
126
+ let shellRendered = false;
127
+ const { pipe, abort } = renderToPipeableStream(
128
+ <I18nextProvider i18n={i18nInstance}>
129
+ <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />
130
+ </I18nextProvider>,
131
+ {
132
+ onAllReady() {
133
+ shellRendered = true;
134
+ const body = new PassThrough();
135
+ const stream = createReadableStreamFromReadable(body);
136
+
137
+ responseHeaders.set('Content-Type', 'text/html');
138
+
139
+ resolve(
140
+ new Response(stream, {
141
+ headers: responseHeaders,
142
+ status: responseStatusCode,
143
+ }),
144
+ );
145
+
146
+ pipe(body);
147
+ },
148
+ onShellError(error: unknown) {
149
+ reject(error);
150
+ },
151
+ onError(error: unknown) {
152
+ responseStatusCode = 500;
153
+ // Log streaming rendering errors from inside the shell. Don't log
154
+ // errors encountered during initial shell rendering since they'll
155
+ // reject and get logged in handleDocumentRequest.
156
+ if (shellRendered) {
157
+ console.error(error);
158
+ }
159
+ },
160
+ },
161
+ );
162
+
163
+ setTimeout(abort, ABORT_DELAY);
164
+ });
165
+ }
166
+
167
+ function handleBrowserRequest(
168
+ request: Request,
169
+ responseStatusCode: number,
170
+ responseHeaders: Headers,
171
+ remixContext: EntryContext,
172
+ loadContext: IAppLoadContext,
173
+ i18nInstance: i18next,
174
+ ) {
175
+ return new Promise((resolve, reject) => {
176
+ let shellRendered = false;
177
+ const slotFillContext = { fills: {} };
178
+ const {
179
+ modules: clientModules,
180
+ container,
181
+ apolloClient: client,
182
+ store,
183
+ } = loadContext;
184
+
185
+ const { pathname, search, hash } = new URL(request.url);
186
+ store.dispatch({
187
+ type: LOCATION_CHANGE,
188
+ payload: { location: { pathname, search, hash }, action: 'POP' },
189
+ });
190
+
191
+ const { pipe, abort } = renderToPipeableStream(
192
+ (
193
+ <I18nextProvider i18n={i18nInstance}>
194
+ <CacheProvider value={cache}>
195
+ <StyleProvider cache={antdCache}>
196
+ <ReduxProvider store={store}>
197
+ <SlotFillProvider context={slotFillContext}>
198
+ <InversifyProvider container={container} modules={clientModules as any}>
199
+ <ApolloProvider client={client}>
200
+ <RemixServer
201
+ context={remixContext}
202
+ url={request.url}
203
+ abortDelay={ABORT_DELAY}
204
+ />
205
+ </ApolloProvider>
206
+ </InversifyProvider>
207
+ </SlotFillProvider>
208
+ </ReduxProvider>
209
+ </StyleProvider>
210
+ </CacheProvider>
211
+ </I18nextProvider>
212
+ ) as any,
213
+ {
214
+ onShellReady() {
215
+ shellRendered = true;
216
+ const body = new PassThrough();
217
+ const stream = createReadableStreamFromReadable(body);
218
+ const apolloState = { ...client.extract() };
219
+ const reduxState = { ...store.getState() };
220
+ const fills = Object.keys(slotFillContext.fills);
221
+ const styleSheet = extractStyle(antdCache);
222
+
223
+ const transform = new ConstantsTransform(fills, apolloState, reduxState, styleSheet);
224
+
225
+ responseHeaders.set('Content-Type', 'text/html');
226
+
227
+ resolve(
228
+ new Response(stream, {
229
+ headers: responseHeaders,
230
+ status: responseStatusCode,
231
+ }),
232
+ );
233
+
234
+ pipe(transform).pipe(renderStylesToNodeStream()).pipe(body);
235
+ },
236
+ onShellError(error: unknown) {
237
+ reject(error);
238
+ },
239
+ onError(error: unknown) {
240
+ responseStatusCode = 500;
241
+ // Log streaming rendering errors from inside the shell. Don't log
242
+ // errors encountered during initial shell rendering since they'll
243
+ // reject and get logged in handleDocumentRequest.
244
+ if (shellRendered) {
245
+ console.error(error);
246
+ }
247
+ reject(error);
248
+ },
249
+ },
250
+ );
251
+
252
+ setTimeout(abort, ABORT_DELAY);
253
+ });
254
+ }
@@ -0,0 +1,107 @@
1
+ import 'reflect-metadata';
2
+ import * as React from 'react';
3
+ import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData, useRouteError, json } from '@remix-run/react';
4
+ // @ts-ignore
5
+ import publicEnv from '@src/config/public-config';
6
+ import { PluginArea } from '@common-stack/client-react';
7
+ import { subscribeReduxRouter } from '@common-stack/remix-router-redux';
8
+ import { ApplicationErrorHandler } from '@admin-layout/ant-ui';
9
+ import { ConfigProvider } from 'antd';
10
+ // @ts-ignore
11
+ import clientModules, { plugins } from '@app/frontend-stack-react/modules.js';
12
+ // @ts-ignore
13
+ import { useChangeLanguage } from 'remix-i18next/react';
14
+ import { useTranslation } from 'react-i18next';
15
+ // @ts-ignore
16
+ import { i18nextInstance as i18next } from '@app/frontend-stack-react/i18n-localization/i18next.server.js';
17
+ // @ts-ignore
18
+ import { ErrorBoundary } from '@app/frontend-stack-react/entries/antui/components/ErrorBoundary';
19
+
20
+ export const loader = async ({ request }) => {
21
+ const locale = await i18next.getLocale(request);
22
+ return json({
23
+ __ENV__: publicEnv,
24
+ locale,
25
+ });
26
+ };
27
+
28
+ export const handle = {
29
+ i18n: 'common',
30
+ };
31
+
32
+ export function shouldRevalidate(params: any) {
33
+ return params.defaultShouldRevalidate && params.currentUrl.pathname !== params.nextUrl.pathname;
34
+ }
35
+
36
+ export function Layout({ children }: { children: React.ReactNode }) {
37
+ const data = useLoaderData<{ locale: any}>();
38
+ const locale = data?.locale;
39
+
40
+ const { i18n } = useTranslation();
41
+
42
+ useChangeLanguage(locale);
43
+
44
+ React.useEffect(() => {
45
+ subscribeReduxRouter({ store: (window as any).__remixStore, router: window.__remixRouter } as any);
46
+ }, []);
47
+
48
+ const getConstants = () => {
49
+ if (typeof window === 'undefined') {
50
+ return (
51
+ <>
52
+ <script
53
+ dangerouslySetInnerHTML={{
54
+ __html: `window.__ENV__ = ${JSON.stringify((data as any)?.__ENV__)}`,
55
+ }}
56
+ />
57
+ <script
58
+ src="https://cdnjs.cloudflare.com/ajax/libs/reflect-metadata/0.1.13/Reflect.min.js"
59
+ integrity="sha512-jvbPH2TH5BSZumEfOJZn9IV+5bSwwN+qG4dvthYe3KCGC3/9HmxZ4phADbt9Pfcp+XSyyfc2vGZ/RMsSUZ9tbQ=="
60
+ crossOrigin="anonymous"
61
+ referrerPolicy="no-referrer"
62
+ ></script>
63
+ <script>window.__APOLLO_STATE__=[__APOLLO_STATE__]</script>
64
+ <script>window.__PRELOADED_STATE__=[__PRELOADED_STATE__]</script>
65
+ <script>window.__SLOT_FILLS__=[__SLOT_FILLS__]</script>
66
+ <script
67
+ dangerouslySetInnerHTML={{
68
+ __html: `if (global === undefined) { var global = window; }`,
69
+ }}
70
+ />
71
+ </>
72
+ );
73
+ }
74
+ return null;
75
+ };
76
+
77
+ return (
78
+ <html lang={locale} dir={i18n.dir()}>
79
+ <head>
80
+ <meta charSet="utf-8" />
81
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
82
+ <Meta />
83
+ <Links />
84
+ {typeof window === 'undefined' ? `[__STYLESHEET__]` : ''}
85
+ </head>
86
+ <body>
87
+ <PluginArea />
88
+ {clientModules.getWrappedRoot(children)}
89
+ <ScrollRestoration />
90
+ <Scripts />
91
+ {getConstants()}
92
+ </body>
93
+ </html>
94
+ );
95
+ }
96
+
97
+ export default function App() {
98
+ return (
99
+ <ApplicationErrorHandler plugins={plugins}>
100
+ <ConfigProvider>
101
+ <Outlet />
102
+ </ConfigProvider>
103
+ </ApplicationErrorHandler>
104
+ );
105
+ }
106
+
107
+ export { ErrorBoundary };
@@ -0,0 +1,20 @@
1
+ // context.tsx
2
+ import React, { createContext } from 'react';
3
+
4
+ export interface ServerStyleContextData {
5
+ key: string;
6
+ ids: Array<string>;
7
+ css: string;
8
+ }
9
+
10
+ export const ServerStyleContext = createContext<
11
+ ServerStyleContextData[] | null
12
+ >(null);
13
+
14
+ export interface ClientStyleContextData {
15
+ reset: () => void;
16
+ }
17
+
18
+ export const ClientStyleContext = createContext<ClientStyleContextData | null>(
19
+ null,
20
+ );