@genesislcap/blank-app-seed 3.30.0-prerelease.15 → 3.30.0-prerelease.16

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed-config",
3
3
  "description": "Genesis Blank App Seed Configuration",
4
- "version": "3.30.0-prerelease.15",
4
+ "version": "3.30.0-prerelease.16",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "lint": "eslint .",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.30.0-prerelease.16](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.30.0-prerelease.15...v3.30.0-prerelease.16) (2024-09-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * add webpack builder in react (#350) 69c4c6b, closes FUI-2173 FUI-2173 FUI-2173 FUI-2157 FUI-2157 origin/ak/FUI-2173 ak/FUI-2157 FUI-2157 FUI-2157 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140 FUI-2140
9
+
3
10
  ## [3.30.0-prerelease.15](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.30.0-prerelease.14...v3.30.0-prerelease.15) (2024-09-26)
4
11
 
5
12
 
@@ -6,10 +6,13 @@
6
6
  "baseline": "npm run clean && npm run bootstrap",
7
7
  "bootstrap": "npm i --legacy-peer-deps",
8
8
  "bootstrap:ci": "npm ci --no-fund --no-audit",
9
- "build": "vite build",
10
9
  "build:stats": "npm run build --stats-json",
11
10
  "clean": "genx clean dist node_modules",
12
- "dev": "vite build && vite preview",
11
+ "dev": "webpack serve --mode development",
12
+ "build": "webpack --mode production",
13
+ "stats": "webpack --mode production --json > stats.json",
14
+ "build:vite": "vite build",
15
+ "dev:vite": "vite build && vite preview",
13
16
  "dev:docker": "npm run dev -- --host 0.0.0.0",
14
17
  "dev:no-open": "NO_OPEN=true npm run dev",
15
18
  "dev:intellij": "npm run dev",
@@ -76,17 +79,23 @@
76
79
  "@typescript-eslint/eslint-plugin": "^7.13.1",
77
80
  "@typescript-eslint/parser": "^7.13.1",
78
81
  "@vitejs/plugin-react": "^4.3.1",
82
+ "babel-loader": "^9.2.1",
79
83
  "babel-plugin-react-require": "^4.0.3",
84
+ "dotenv-webpack": "^8.1.0",
80
85
  "eslint": "^8.57.0",
81
86
  "eslint-plugin-react-hooks": "^4.6.2",
82
87
  "eslint-plugin-react-refresh": "^0.4.7",
88
+ "file-loader": "^6.2.0",
83
89
  "jest": "^29.7.0",
84
90
  "jest-environment-jsdom": "^29.7.0",
85
91
  "ts-jest": "^29.2.5",
86
92
  "ts-node": "^10.9.2",
87
93
  "typescript": "^5.2.2",
88
94
  "vite": "^5.3.1",
89
- "vite-plugin-tsconfig-paths": "^1.4.1"
95
+ "vite-plugin-tsconfig-paths": "^1.4.1",
96
+ "webpack": "^5.94.0",
97
+ "webpack-cli": "^5.1.4",
98
+ "webpack-dev-server": "^5.1.0"
90
99
  },
91
100
  "overrides": {
92
101
  "@types/react": "npm:types-react@alpha",
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>{{capitalCase appName}}</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script src="/bundle.js"></script>
12
+ </body>
13
+ </html>
@@ -56,7 +56,12 @@ const DynamicLayout = () => {
56
56
 
57
57
  };
58
58
 
59
- const App: React.FC = ({ rootElement }) => {
59
+ interface AppProps {
60
+ rootElement: HTMLElement;
61
+ }
62
+
63
+ const App: React.FC<AppProps> = ({ rootElement }) => {
64
+ const [isStoreConnected, setIsStoreConnected] = useState(false);
60
65
  const dispatchCustomEvent = (type: string, detail?: any) => {
61
66
  rootElement.dispatchEvent(customEventFactory(type, detail));
62
67
  };
@@ -80,24 +85,29 @@ const App: React.FC = ({ rootElement }) => {
80
85
  configureFoundationLogin({ router: history });
81
86
 
82
87
  useEffect(() => {
83
- rootElement.addEventListener('store-connected', handleStoreConnected);
84
88
  registerStylesTarget(document.body, 'main');
85
89
  {{#if FDC3.channels.length~}}
86
90
  onFDC3Ready(FDC3ReadyHandler);
87
91
  {{/if}}
88
- dispatchCustomEvent('store-connected', rootElement);
89
- dispatchCustomEvent('store-ready', true);
92
+ if (!isStoreConnected) {
93
+ rootElement.addEventListener('store-connected', handleStoreConnected);
94
+ dispatchCustomEvent('store-connected', rootElement);
95
+ dispatchCustomEvent('store-ready', true);
96
+ setIsStoreConnected(true);
97
+ }
90
98
 
91
99
  return () => {
92
- rootElement.removeEventListener('store-connected', handleStoreConnected);
93
- dispatchCustomEvent('store-disconnected');
100
+ if (isStoreConnected) {
101
+ rootElement.removeEventListener('store-connected', handleStoreConnected);
102
+ dispatchCustomEvent('store-disconnected');
103
+ }
94
104
  };
95
- }, []);
105
+ }, [isStoreConnected]);
96
106
 
97
107
  return (
98
108
  <AuthProvider>
99
109
  <RoutesProvider>
100
- <HistoryRouter history={history}>
110
+ <HistoryRouter history={history as any}>
101
111
  <Routes>
102
112
  <Route path="*" element={<DynamicLayout />} />
103
113
  </Routes>
@@ -1,4 +1,5 @@
1
1
  import { RouteLayouts } from './types/RouteLayouts';
2
+ import { environment } from '@environment';
2
3
 
3
4
  export const routeLayouts: RouteLayouts = {
4
5
  '/auth': 'blank',
@@ -9,7 +10,7 @@ export const AUTH_PATH = 'auth';
9
10
  export const NOT_PERMITTED_PATH = 'not-permitted';
10
11
 
11
12
  export const API_DATA = {
12
- URL: import.meta.env.VITE_API_HOST,
13
+ URL: environment.API_HOST,
13
14
  AUTH: {
14
15
  username: '', // provide login to a user in given environment
15
16
  password: '', // provide password to a user in given environment
@@ -1,10 +1,11 @@
1
1
  import React, { useState, DOMAttributes } from 'react';
2
2
 
3
- type CustomElement<T = HTMLElement> = Partial<T & DOMAttributes<T> & { children: any }>;
3
+ type CustomElement<T = React.HTMLAttributes<HTMLElement>> = Partial<T & DOMAttributes<T> & { [key: string]: any }>;
4
4
 
5
5
  declare module "react/jsx-runtime" {
6
6
  namespace JSX {
7
7
  interface IntrinsicElements {
8
+ 'rapid-design-system-provider': CustomElement;
8
9
  'entity-management': CustomElement;
9
10
  'foundation-form': CustomElement;
10
11
  'rapid-grid-pro': CustomElement;
@@ -16,6 +17,7 @@ declare module "react/jsx-runtime" {
16
17
  'rapid-layout': CustomElement;
17
18
  'rapid-layout-region': CustomElement;
18
19
  'rapid-layout-item': CustomElement;
20
+ 'foundation-header': CustomElement;
19
21
  }
20
22
  }
21
23
  }
@@ -0,0 +1,8 @@
1
+ export const environment = {
2
+ production: true,
3
+ PORT: 4200,
4
+ ENABLE_SSO: {{enableSSO}},
5
+ HOST: 'localhost',
6
+ PROTOCOL: 'http',
7
+ BUILDER: 'webpack',
8
+ };
@@ -0,0 +1,9 @@
1
+ export const environment = {
2
+ production: false,
3
+ API_HOST: '{{apiHost}}',
4
+ PORT: 4200,
5
+ ENABLE_SSO: {{enableSSO}},
6
+ HOST: 'localhost',
7
+ PROTOCOL: 'http',
8
+ BUILDER: 'webpack'
9
+ };
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect, ReactNode } from 'react';
2
+ import { RouteObject } from 'react-router';
2
3
  import isConnectedHelper from '@/helpers/isConnectedHelper';
3
4
  import isAuthenticatedHelper from '@/helpers/isAuthenticatedHelper';
4
5
  import hasPermissionHelper from '@/helpers/hasPermissionHelper';
@@ -16,8 +17,15 @@ const redirectUrlByPermissionState: { [key in Partial<PermissionState>]?: string
16
17
  [PermissionState.UNKNOWN]: AUTH_PATH,
17
18
  };
18
19
 
20
+ type ExtendedRouteObject = RouteObject & {
21
+ data?: {
22
+ permissionCode?: string;
23
+ };
24
+ path: string;
25
+ }
26
+
19
27
  const ProtectedGuard: React.FC<{ children: ReactNode }> = ({ children }: { children: ReactNode }) => {
20
- const routes = useRoutesContext();
28
+ const routes = useRoutesContext() as ExtendedRouteObject[];
21
29
  const [isConnected, setIsConnected] = useState<boolean | null>(null);
22
30
  const isAuthenticated: boolean | null = null;
23
31
  const route = routes.find(({ path }) => path === location.pathname);
@@ -1,4 +1,5 @@
1
1
  import React, { ReactNode, useEffect, useRef } from 'react';
2
+ import { RouteObject } from 'react-router';
2
3
  import { configureDesignSystem, getNavItems } from '@genesislcap/foundation-ui';
3
4
  import { useNavigate } from 'react-router-dom';
4
5
  import {
@@ -14,11 +15,18 @@ interface DefaultLayoutProps {
14
15
  children: ReactNode;
15
16
  }
16
17
 
18
+ type ExtendedRouteObject = RouteObject & {
19
+ data?: {
20
+ navItems?: any;
21
+ };
22
+ path: string;
23
+ }
24
+
17
25
  const DefaultLayout: React.FC<DefaultLayoutProps> = ({ children }) => {
18
26
  const navigate = useNavigate();
19
27
  const designSystemProviderRef = useRef<HTMLElement>(null);
20
28
  const foundationHeaderRef = useRef<HTMLElement>(null);
21
- const routes = useRoutesContext();
29
+ const routes = useRoutesContext() as ExtendedRouteObject[];
22
30
  const navItems = getNavItems(routes.flatMap((route) => ({
23
31
  path: route.path || '',
24
32
  navItems: route.data?.navItems,
@@ -67,7 +75,7 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = ({ children }) => {
67
75
  show-luminance-toggle-button
68
76
  show-misc-toggle-button
69
77
  routeNavItems={navItems}
70
- navigateTo={(path) => navigate(path)}
78
+ navigateTo={(path: string) => navigate(path)}
71
79
  >
72
80
  </foundation-header>
73
81
  <section className={styles['content']}>
@@ -11,11 +11,13 @@ const logger = createLogger('main');
11
11
 
12
12
  function bootstrapApp() {
13
13
  const rootEelement = document.getElementById('root');
14
- ReactDOM.createRoot(rootEelement!).render(
15
- <React.StrictMode>
16
- <App rootElement={rootEelement} />
17
- </React.StrictMode>,
18
- )
14
+ if (rootEelement) {
15
+ ReactDOM.createRoot(rootEelement!).render(
16
+ <React.StrictMode>
17
+ <App rootElement={rootEelement} />
18
+ </React.StrictMode>,
19
+ )
20
+ }
19
21
  }
20
22
 
21
23
  registerPBCs()
@@ -1,12 +1,19 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useEffect, useRef, RouteObject } from 'react';
2
2
  import { deriveElementTag } from './utils';
3
3
  import { useRoutesContext } from '@/store/RoutesContext';
4
4
  import { useLocation } from 'react-router-dom';
5
5
 
6
+ type ExtendedRouteObject = RouteObject & {
7
+ data?: {
8
+ pbcElementTag?: string;
9
+ pbcElement?: any;
10
+ };
11
+ path: string;
12
+ }
6
13
 
7
14
  const PBCContainer: React.FC = () => {
8
15
  const containerRef = useRef<HTMLDivElement>(null);
9
- const routes = useRoutesContext();
16
+ const routes = useRoutesContext() as ExtendedRouteObject[];
10
17
  const location = useLocation();
11
18
 
12
19
  useEffect(() => {
@@ -21,6 +28,7 @@ const PBCContainer: React.FC = () => {
21
28
  const element = pbcElement.define ? pbcElement : await pbcElement();
22
29
  const tagName = pbcElementTag || deriveElementTag(element.name);
23
30
  const customElement = document.createElement(tagName);
31
+
24
32
  if (containerRef.current) {
25
33
  containerRef.current.replaceChildren();
26
34
  containerRef.current.appendChild(customElement);
@@ -45,4 +45,4 @@ class StoreService {
45
45
  }
46
46
  }
47
47
 
48
- export const storeService = new StoreService();
48
+ export const storeService = new StoreService();
@@ -58,6 +58,7 @@ export const RoutesProvider: React.FC<{ children: ReactNode }> = ({ children })
58
58
  data: {
59
59
  ...route.settings,
60
60
  pbcElement: route.element,
61
+ // @ts-expect-error - getApp() is not typed to return the elementTag
61
62
  pbcElementTag: route.elementTag,
62
63
  navItems: route.navItems,
63
64
  },
@@ -1,5 +1,5 @@
1
- import { createBrowserHistory, BrowserHistory } from 'history';
1
+ import { createBrowserHistory, History } from 'history';
2
2
 
3
- export type Router = BrowserHistory
3
+ export type Router = History;
4
4
 
5
- export const history: Router = createBrowserHistory();
5
+ export const history = createBrowserHistory() as History;
@@ -2,5 +2,4 @@ export * from './history';
2
2
  export * from './fdc3';
3
3
  export * from './permissions';
4
4
  export * from './setApiHost';
5
- export * from './store';
6
5
  export * from './getLayoutNameByRoute';
@@ -23,6 +23,8 @@
23
23
  "paths": {
24
24
  "@/*": ["src/*"]
25
25
  },
26
+ "experimentalDecorators": true,
27
+ "emitDecoratorMetadata": true
26
28
  },
27
29
  "include": [
28
30
  "src" ,
@@ -1,7 +1,6 @@
1
1
  import { fileURLToPath } from 'node:url';
2
2
  import { resolve, dirname } from 'path';
3
3
  import { defineConfig, UserConfig } from 'vite';
4
- import fs from 'fs';
5
4
  import react from '@vitejs/plugin-react';
6
5
  import visualizer from 'rollup-plugin-visualizer';
7
6
  import tsconfigPaths from 'vite-plugin-tsconfig-paths';
@@ -11,21 +10,13 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
11
10
  export default defineConfig(({ mode }: { mode: string }): UserConfig => {
12
11
  const https: boolean = process.env.HTTPS === 'true';
13
12
  const open: boolean = !(process.env.NO_OPEN === 'true');
14
- const jsonFilePath: string = resolve(process.cwd(), `env.${mode}.json`);
15
- const envConfig: Record<string, string> = {};
16
-
17
- if (fs.existsSync(jsonFilePath)) {
18
- const jsonContent: string = fs.readFileSync(jsonFilePath, 'utf-8');
19
- const parsedConfig: Record<string, any> = JSON.parse(jsonContent);
20
-
21
- for (const key in parsedConfig) {
22
- envConfig[`import.meta.env.${key}`] = JSON.stringify(parsedConfig[key]);
23
- }
24
- }
13
+ const environmentFile = mode === 'production'
14
+ ? 'environment.prod.ts'
15
+ : 'environment.ts';
16
+ const environmentPath = resolve(__dirname, 'src/environments', environmentFile);
25
17
 
26
18
  const config: UserConfig = {
27
19
  define: {
28
- ...envConfig,
29
20
  BUILDER: JSON.stringify('vite'),
30
21
  },
31
22
  server: {
@@ -42,12 +33,14 @@ export default defineConfig(({ mode }: { mode: string }): UserConfig => {
42
33
  build: {
43
34
  rollupOptions: {
44
35
  plugins: [],
45
- treeshake: false, // Disable tree-shaking because it's causing issues with the Foundation Router
36
+ treeshake: false,
46
37
  },
47
38
  },
48
39
  resolve: {
49
40
  alias: {
50
41
  'foundationZero/ZeroDesignSystem': resolve(__dirname, 'node_modules/@genesislcap/foundation-zero'),
42
+ '@': resolve(__dirname, 'src'),
43
+ '@environment': environmentPath,
51
44
  'pbc': resolve(__dirname, 'src/pbc'),
52
45
  },
53
46
  preserveSymlinks: true,
@@ -0,0 +1,103 @@
1
+ const { resolve, join } = require('path');
2
+ const webpack = require('webpack');
3
+ const Dotenv = require('dotenv-webpack');
4
+
5
+ module.exports = (env, argv) => {
6
+ const mode = argv.mode || 'development';
7
+ const https = process.env.HTTPS === 'true';
8
+ const open = !(process.env.NO_OPEN === 'true');
9
+ const environmentFile = mode === 'production'
10
+ ? 'environment.prod.ts'
11
+ : 'environment.ts';
12
+ const environmentPath = resolve(__dirname, 'src/environments', environmentFile);
13
+
14
+ return {
15
+ mode,
16
+ entry: './src/main.tsx',
17
+ output: {
18
+ path: resolve(__dirname, 'dist'),
19
+ filename: 'bundle.js',
20
+ },
21
+ resolve: {
22
+ extensions: ['.tsx', '.ts', '.js'],
23
+ alias: {
24
+ 'foundationZero/ZeroDesignSystem': resolve(__dirname, 'node_modules/@genesislcap/foundation-zero'),
25
+ '@': resolve(__dirname, 'src'),
26
+ '@environment': environmentPath,
27
+ 'pbc': resolve(__dirname, 'src/pbc'),
28
+ },
29
+ symlinks: true,
30
+ },
31
+ module: {
32
+ rules: [
33
+ {
34
+ test: /\.tsx?$/,
35
+ use: {
36
+ loader: 'ts-loader',
37
+ options: {
38
+ transpileOnly: true,
39
+ },
40
+ },
41
+ exclude: /node_modules/,
42
+ },
43
+ {
44
+ test: /\.jsx?$/,
45
+ exclude: /node_modules/,
46
+ use: {
47
+ loader: 'babel-loader',
48
+ options: {
49
+ presets: ['@babel/preset-env', '@babel/preset-react'],
50
+ },
51
+ },
52
+ },
53
+ {
54
+ test: /\.css$/,
55
+ use: ['style-loader', 'css-loader'],
56
+ },
57
+ {
58
+ test: /\.svg$/,
59
+ type: 'asset/resource',
60
+ },
61
+ {
62
+ test: /\.(png|jpe?g|gif)$/i,
63
+ use: [
64
+ {
65
+ loader: 'file-loader',
66
+ },
67
+ ],
68
+ },
69
+ ],
70
+ },
71
+ plugins: [
72
+ new Dotenv(),
73
+ new webpack.DefinePlugin({
74
+ BUILDER: JSON.stringify('webpack'),
75
+ }),
76
+ ],
77
+ devServer: {
78
+ server: https ? 'https' : 'http',
79
+ open,
80
+ static: {
81
+ directory: join(__dirname, 'public'),
82
+ },
83
+ client: {
84
+ overlay: {
85
+ errors: false,
86
+ warnings: false,
87
+ runtimeErrors: false,
88
+ },
89
+ },
90
+ compress: true,
91
+ port: 3000,
92
+ historyApiFallback: true,
93
+ },
94
+ stats: {
95
+ all: false,
96
+ errors: true,
97
+ warnings: true,
98
+ timings: true,
99
+ assets: true,
100
+ modules: false,
101
+ },
102
+ };
103
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed",
3
3
  "description": "Genesis Blank App Seed",
4
- "version": "3.30.0-prerelease.15",
4
+ "version": "3.30.0-prerelease.16",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "release": "semantic-release"
@@ -1,3 +0,0 @@
1
- {
2
- "VITE_API_HOST": "{{apiHost}}"
3
- }
@@ -1,34 +0,0 @@
1
- import { CustomEventMap } from '@genesislcap/foundation-events';
2
- import { getApp } from '@genesislcap/foundation-shell/app';
3
- import {
4
- AbstractStoreRoot,
5
- registerStore,
6
- StoreRoot,
7
- StoreRootEventDetailMap,
8
- } from '@genesislcap/foundation-store';
9
- import { DI } from '@genesislcap/web-core';
10
-
11
- export interface Store extends StoreRoot {}
12
-
13
- export type StoreEventDetailMap = StoreRootEventDetailMap & {};
14
-
15
- declare global {
16
- interface HTMLElementEventMap extends CustomEventMap<StoreEventDetailMap> {}
17
- }
18
-
19
- class DefaultStore extends AbstractStoreRoot<Store, StoreEventDetailMap> implements Store {
20
- constructor() {
21
- super();
22
-
23
- /**
24
- * Register the store root
25
- */
26
- getApp().registerStoreRoot(this);
27
- }
28
- }
29
-
30
- export const Store = registerStore(DefaultStore, 'Store');
31
-
32
- export function getStore(): Store {
33
- return DI.getOrCreateDOMContainer().get(Store) as Store;
34
- }