@aws/nx-plugin 0.0.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.
Files changed (142) hide show
  1. package/README.md +63 -0
  2. package/generators.json +52 -0
  3. package/package.json +27 -0
  4. package/src/cloudscape-website/app/README.md +253 -0
  5. package/src/cloudscape-website/app/__snapshots__/generator.spec.ts.snap +539 -0
  6. package/src/cloudscape-website/app/files/app/src/config.ts.template +4 -0
  7. package/src/cloudscape-website/app/files/app/src/layouts/App/index.tsx.template +132 -0
  8. package/src/cloudscape-website/app/files/app/src/layouts/App/navitems.ts.template +8 -0
  9. package/src/cloudscape-website/app/files/app/src/layouts/Routes/index.tsx.template +18 -0
  10. package/src/cloudscape-website/app/files/app/src/main.tsx.template +22 -0
  11. package/src/cloudscape-website/app/files/app/src/pages/Home/index.tsx.template +25 -0
  12. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/cloudfront-web-acl.ts.template +317 -0
  13. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/index.ts.template +4 -0
  14. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/static-website.ts.template +237 -0
  15. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/webacl_event_handler/index.ts.template +301 -0
  16. package/src/cloudscape-website/app/files/e2e/cypress/src/e2e/app.cy.ts.template +13 -0
  17. package/src/cloudscape-website/app/files/e2e/cypress/src/support/app.po.ts.template +1 -0
  18. package/src/cloudscape-website/app/files/e2e/playwright/src/example.spec.ts.template +6 -0
  19. package/src/cloudscape-website/app/generator.d.ts +4 -0
  20. package/src/cloudscape-website/app/generator.js +177 -0
  21. package/src/cloudscape-website/app/generator.js.map +1 -0
  22. package/src/cloudscape-website/app/schema.d.js +6 -0
  23. package/src/cloudscape-website/app/schema.d.js.map +1 -0
  24. package/src/cloudscape-website/app/schema.d.ts +35 -0
  25. package/src/cloudscape-website/app/schema.json +189 -0
  26. package/src/cloudscape-website/cognito-auth/README.md +172 -0
  27. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +238 -0
  28. package/src/cloudscape-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +50 -0
  29. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/index.ts.template +4 -0
  30. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/user-identity.ts.template +69 -0
  31. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/userpool-with-mfa.ts.template +70 -0
  32. package/src/cloudscape-website/cognito-auth/generator.d.ts +4 -0
  33. package/src/cloudscape-website/cognito-auth/generator.js +100 -0
  34. package/src/cloudscape-website/cognito-auth/generator.js.map +1 -0
  35. package/src/cloudscape-website/cognito-auth/schema.d.js +6 -0
  36. package/src/cloudscape-website/cognito-auth/schema.d.js.map +1 -0
  37. package/src/cloudscape-website/cognito-auth/schema.d.ts +4 -0
  38. package/src/cloudscape-website/cognito-auth/schema.json +36 -0
  39. package/src/cloudscape-website/runtime-config/__snapshots__/generator.spec.ts.snap +112 -0
  40. package/src/cloudscape-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +46 -0
  41. package/src/cloudscape-website/runtime-config/generator.d.ts +4 -0
  42. package/src/cloudscape-website/runtime-config/generator.js +74 -0
  43. package/src/cloudscape-website/runtime-config/generator.js.map +1 -0
  44. package/src/cloudscape-website/runtime-config/schema.d.js +6 -0
  45. package/src/cloudscape-website/runtime-config/schema.d.js.map +1 -0
  46. package/src/cloudscape-website/runtime-config/schema.d.ts +3 -0
  47. package/src/cloudscape-website/runtime-config/schema.json +19 -0
  48. package/src/gitlab/files/.gitlab-ci.yml.template +26 -0
  49. package/src/gitlab/generator.d.ts +4 -0
  50. package/src/gitlab/generator.js +26 -0
  51. package/src/gitlab/generator.js.map +1 -0
  52. package/src/gitlab/schema.d.js +6 -0
  53. package/src/gitlab/schema.d.js.map +1 -0
  54. package/src/gitlab/schema.d.ts +5 -0
  55. package/src/gitlab/schema.json +52 -0
  56. package/src/index.d.ts +0 -0
  57. package/src/index.js +3 -0
  58. package/src/index.js.map +1 -0
  59. package/src/infra/app/README.md +175 -0
  60. package/src/infra/app/__snapshots__/generator.spec.ts.snap +864 -0
  61. package/src/infra/app/files/cdk.json +67 -0
  62. package/src/infra/app/files/src/main.ts.template +37 -0
  63. package/src/infra/app/files/src/stacks/application-stack.ts.template +10 -0
  64. package/src/infra/app/generator.d.ts +4 -0
  65. package/src/infra/app/generator.js +75 -0
  66. package/src/infra/app/generator.js.map +1 -0
  67. package/src/infra/app/schema.d.js +6 -0
  68. package/src/infra/app/schema.d.js.map +1 -0
  69. package/src/infra/app/schema.d.ts +6 -0
  70. package/src/infra/app/schema.json +35 -0
  71. package/src/trpc/backend/README.md +549 -0
  72. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +110 -0
  73. package/src/trpc/backend/files/backend/src/index.ts.template +1 -0
  74. package/src/trpc/backend/files/backend/src/lambdas/index.ts.template +1 -0
  75. package/src/trpc/backend/files/backend/src/lambdas/middleware.ts.template +146 -0
  76. package/src/trpc/backend/files/backend/src/lambdas/router.ts.template +36 -0
  77. package/src/trpc/backend/files/common/constructs/src/__apiNameKebabCase__/index.ts.template +64 -0
  78. package/src/trpc/backend/files/schema/src/index.ts.template +7 -0
  79. package/src/trpc/backend/generator.d.ts +4 -0
  80. package/src/trpc/backend/generator.js +128 -0
  81. package/src/trpc/backend/generator.js.map +1 -0
  82. package/src/trpc/backend/schema.d.js +6 -0
  83. package/src/trpc/backend/schema.d.js.map +1 -0
  84. package/src/trpc/backend/schema.d.ts +8 -0
  85. package/src/trpc/backend/schema.json +44 -0
  86. package/src/trpc/react/README.md +320 -0
  87. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +98 -0
  88. package/src/trpc/react/files/src/components/TRPCClientProvider/index.tsx.template +34 -0
  89. package/src/trpc/react/files/src/hooks/useTrpc.tsx.template +5 -0
  90. package/src/trpc/react/generator.d.ts +4 -0
  91. package/src/trpc/react/generator.js +81 -0
  92. package/src/trpc/react/generator.js.map +1 -0
  93. package/src/trpc/react/schema.d.js +6 -0
  94. package/src/trpc/react/schema.d.js.map +1 -0
  95. package/src/trpc/react/schema.d.ts +5 -0
  96. package/src/trpc/react/schema.json +32 -0
  97. package/src/ts/cjs-to-esm/generator.d.ts +8 -0
  98. package/src/ts/cjs-to-esm/generator.js +201 -0
  99. package/src/ts/cjs-to-esm/generator.js.map +1 -0
  100. package/src/ts/cjs-to-esm/schema.d.js +6 -0
  101. package/src/ts/cjs-to-esm/schema.d.js.map +1 -0
  102. package/src/ts/cjs-to-esm/schema.d.ts +5 -0
  103. package/src/ts/cjs-to-esm/schema.json +28 -0
  104. package/src/ts/lib/README.md +149 -0
  105. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +260 -0
  106. package/src/ts/lib/eslint.d.ts +3 -0
  107. package/src/ts/lib/eslint.js +41 -0
  108. package/src/ts/lib/eslint.js.map +1 -0
  109. package/src/ts/lib/files/src/index.ts.template +3 -0
  110. package/src/ts/lib/generator.d.ts +21 -0
  111. package/src/ts/lib/generator.js +61 -0
  112. package/src/ts/lib/generator.js.map +1 -0
  113. package/src/ts/lib/schema.d.js +6 -0
  114. package/src/ts/lib/schema.d.js.map +1 -0
  115. package/src/ts/lib/schema.d.ts +13 -0
  116. package/src/ts/lib/schema.json +46 -0
  117. package/src/ts/lib/ts-project-utils.d.ts +6 -0
  118. package/src/ts/lib/ts-project-utils.js +107 -0
  119. package/src/ts/lib/ts-project-utils.js.map +1 -0
  120. package/src/ts/lib/types.d.ts +10 -0
  121. package/src/ts/lib/types.js +6 -0
  122. package/src/ts/lib/types.js.map +1 -0
  123. package/src/ts/lib/vitest.d.ts +3 -0
  124. package/src/ts/lib/vitest.js +67 -0
  125. package/src/ts/lib/vitest.js.map +1 -0
  126. package/src/utils/files/common/constructs/src/index.ts.template +1 -0
  127. package/src/utils/files/common/constructs/src/runtime-config/index.ts.template +1 -0
  128. package/src/utils/files/common/constructs/src/runtime-config/runtime-config.ts.template +33 -0
  129. package/src/utils/files/common/types/src/index.ts.template +1 -0
  130. package/src/utils/files/common/types/src/runtime-config.ts.template +13 -0
  131. package/src/utils/npm-scope.d.ts +7 -0
  132. package/src/utils/npm-scope.js +37 -0
  133. package/src/utils/npm-scope.js.map +1 -0
  134. package/src/utils/paths.d.ts +3 -0
  135. package/src/utils/paths.js +32 -0
  136. package/src/utils/paths.js.map +1 -0
  137. package/src/utils/shared-constructs.d.ts +7 -0
  138. package/src/utils/shared-constructs.js +72 -0
  139. package/src/utils/shared-constructs.js.map +1 -0
  140. package/src/utils/versions.d.ts +31 -0
  141. package/src/utils/versions.js +49 -0
  142. package/src/utils/versions.js.map +1 -0
@@ -0,0 +1,539 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`cloudscape-website generator > should configure TypeScript correctly > tsconfig.json 1`] = `
4
+ {
5
+ "compilerOptions": {
6
+ "allowJs": false,
7
+ "allowSyntheticDefaultImports": true,
8
+ "esModuleInterop": false,
9
+ "jsx": "react-jsx",
10
+ "module": "Preserve",
11
+ "moduleResolution": "Bundler",
12
+ "strict": true,
13
+ "types": [
14
+ "vite/client",
15
+ "vitest",
16
+ ],
17
+ },
18
+ "extends": "../tsconfig.base.json",
19
+ "files": [],
20
+ "include": [],
21
+ "references": [
22
+ {
23
+ "path": "./tsconfig.app.json",
24
+ },
25
+ {
26
+ "path": "./tsconfig.spec.json",
27
+ },
28
+ ],
29
+ }
30
+ `;
31
+
32
+ exports[`cloudscape-website generator > should configure vite correctly > vite.config.ts 1`] = `
33
+ "/// <reference types='vitest' />
34
+ import { defineConfig } from 'vite';
35
+ import react from '@vitejs/plugin-react';
36
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
37
+ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
38
+ export default defineConfig({
39
+ define: {
40
+ global: {},
41
+ },
42
+ root: __dirname,
43
+ cacheDir: '../node_modules/.vite/test-app',
44
+ server: {
45
+ port: 4200,
46
+ host: 'localhost',
47
+ },
48
+ preview: {
49
+ port: 4300,
50
+ host: 'localhost',
51
+ },
52
+ plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
53
+ build: {
54
+ outDir: '../dist/test-app',
55
+ emptyOutDir: true,
56
+ reportCompressedSize: true,
57
+ commonjsOptions: {
58
+ transformMixedEsModules: true,
59
+ },
60
+ },
61
+ test: {
62
+ watch: false,
63
+ globals: true,
64
+ environment: 'jsdom',
65
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
66
+ reporters: ['default'],
67
+ coverage: {
68
+ reportsDirectory: '../coverage/test-app',
69
+ provider: 'v8',
70
+ },
71
+ passWithNoTests: true,
72
+ },
73
+ });
74
+ "
75
+ `;
76
+
77
+ exports[`cloudscape-website generator > should generate base files and structure > app-layout.tsx 1`] = `
78
+ "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
79
+ SPDX-License-Identifier: Apache-2.0 */
80
+ import { useCognitoAuthContext } from '@aws-northstar/ui';
81
+ import getBreadcrumbs from '@aws-northstar/ui/components/AppLayout/utils/getBreadcrumbs';
82
+ import NavHeader from '@aws-northstar/ui/components/AppLayout/components/NavHeader';
83
+ import * as React from 'react';
84
+ import { createContext, useCallback, useEffect, useState } from 'react';
85
+ import { NavItems } from './navitems';
86
+ import Config from '../../config';
87
+ import Routes from '../Routes';
88
+
89
+ import {
90
+ BreadcrumbGroup,
91
+ BreadcrumbGroupProps,
92
+ SideNavigation,
93
+ } from '@cloudscape-design/components';
94
+ import AppLayout, {
95
+ AppLayoutProps,
96
+ } from '@cloudscape-design/components/app-layout';
97
+ import { useLocation, useNavigate } from 'react-router-dom';
98
+
99
+ /**
100
+ * Context for updating/retrieving the AppLayout.
101
+ */
102
+ export const AppLayoutContext = createContext({
103
+ appLayoutProps: {},
104
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
105
+ setAppLayoutProps: (_: AppLayoutProps) => {},
106
+ });
107
+
108
+ /**
109
+ * Defines the App layout and contains logic for routing.
110
+ */
111
+ const App: React.FC = () => {
112
+ const [username, setUsername] = useState<string | undefined>();
113
+ const [email, setEmail] = useState<string | undefined>();
114
+ const { getAuthenticatedUser } = useCognitoAuthContext();
115
+
116
+ const navigate = useNavigate();
117
+ const [activeHref, setActiveHref] = useState('/');
118
+ const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
119
+ BreadcrumbGroupProps.Item[]
120
+ >([{ text: '/', href: '/' }]);
121
+ const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
122
+ const location = useLocation();
123
+
124
+ useEffect(() => {
125
+ const authUser = getAuthenticatedUser();
126
+ setUsername(authUser?.getUsername());
127
+
128
+ authUser?.getSession(() => {
129
+ authUser.getUserAttributes((_, attributes) => {
130
+ setEmail(attributes?.find((a) => a.Name === 'email')?.Value);
131
+ });
132
+ });
133
+ }, [getAuthenticatedUser, setUsername, setEmail]);
134
+
135
+ const setAppLayoutPropsSafe = useCallback(
136
+ (props: AppLayoutProps) => {
137
+ JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
138
+ setAppLayoutProps(props);
139
+ },
140
+ [appLayoutProps]
141
+ );
142
+
143
+ useEffect(() => {
144
+ setActiveHref(location.pathname);
145
+ const breadcrumbs = getBreadcrumbs(location.pathname, location.search, '/');
146
+ setActiveBreadcrumbs(breadcrumbs);
147
+ }, [location]);
148
+
149
+ const onNavigate = useCallback(
150
+ (e: CustomEvent<{ href: string; external?: boolean }>) => {
151
+ if (!e.detail.external) {
152
+ e.preventDefault();
153
+ setAppLayoutPropsSafe({
154
+ contentType: undefined,
155
+ splitPanelOpen: false,
156
+ splitPanelSize: undefined,
157
+ splitPanelPreferences: undefined,
158
+ });
159
+ navigate(e.detail.href);
160
+ }
161
+ },
162
+ [navigate, setAppLayoutPropsSafe]
163
+ );
164
+
165
+ return (
166
+ <AppLayoutContext.Provider
167
+ value={{ appLayoutProps, setAppLayoutProps: setAppLayoutPropsSafe }}
168
+ >
169
+ <NavHeader
170
+ title={Config.applicationName}
171
+ logo={Config.logo}
172
+ user={
173
+ username
174
+ ? {
175
+ username,
176
+ email,
177
+ }
178
+ : undefined
179
+ }
180
+ onSignout={() =>
181
+ new Promise(() => {
182
+ getAuthenticatedUser()?.signOut();
183
+ window.location.href = '/';
184
+ })
185
+ }
186
+ />
187
+ <AppLayout
188
+ breadcrumbs={
189
+ <BreadcrumbGroup onFollow={onNavigate} items={activeBreadcrumbs} />
190
+ }
191
+ toolsHide
192
+ navigation={
193
+ <SideNavigation
194
+ header={{ text: Config.applicationName, href: '/' }}
195
+ activeHref={activeHref}
196
+ onFollow={onNavigate}
197
+ items={NavItems}
198
+ />
199
+ }
200
+ content={<Routes />}
201
+ splitPanelOpen={false}
202
+ splitPanelPreferences={{ position: 'bottom' }}
203
+ {...appLayoutProps}
204
+ />
205
+ </AppLayoutContext.Provider>
206
+ );
207
+ };
208
+
209
+ export default App;
210
+ "
211
+ `;
212
+
213
+ exports[`cloudscape-website generator > should generate base files and structure > config.ts 1`] = `
214
+ "export default {
215
+ applicationName: 'test-app',
216
+ logo: 'data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyBmaWxsPSIjMjQ4YmFlIiB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0cm9rZT0iIzI0OGJhZSI+Cg08ZyBpZD0iU1ZHUmVwb19iZ0NhcnJpZXIiIHN0cm9rZS13aWR0aD0iMCIvPgoNPGcgaWQ9IlNWR1JlcG9fdHJhY2VyQ2FycmllciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cg08ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+Cg08dGl0bGU+aW9uaWNvbnMtdjVfbG9nb3M8L3RpdGxlPgoNPHBhdGggZD0iTTQxMC42NiwxODAuNzJoMHEtNy42Ny0yLjYyLTE1LjQ1LTQuODgsMS4yOS01LjI1LDIuMzgtMTAuNTZjMTEuNy01Ni45LDQuMDUtMTAyLjc0LTIyLjA2LTExNy44My0yNS0xNC40OC02NiwuNjEtMTA3LjM2LDM2LjY5cS02LjEsNS4zNC0xMS45NSwxMS0zLjktMy43Ni04LTcuMzZjLTQzLjM1LTM4LjU4LTg2LjgtNTQuODMtMTEyLjg4LTM5LjY5LTI1LDE0LjUxLTMyLjQzLDU3LjYtMjEuOSwxMTEuNTNxMS41OCw4LDMuNTUsMTUuOTNjLTYuMTUsMS43NS0xMi4wOSwzLjYyLTE3Ljc3LDUuNkM0OC40NiwxOTguOSwxNiwyMjYuNzMsMTYsMjU1LjU5YzAsMjkuODIsMzQuODQsNTkuNzIsODcuNzcsNzcuODVxNi40NCwyLjE5LDEzLDQuMDdRMTE0LjY0LDM0NiwxMTMsMzU0LjY4Yy0xMCw1My0yLjIsOTUuMDcsMjIuNzUsMTA5LjQ5LDI1Ljc3LDE0Ljg5LDY5LS40MSwxMTEuMTQtMzcuMzFxNS00LjM4LDEwLTkuMjUsNi4zMiw2LjExLDEzLDExLjg2YzQwLjgsMzUuMTgsODEuMDksNDkuMzksMTA2LDM0LjkzLDI1Ljc1LTE0Ljk0LDM0LjEyLTYwLjE0LDIzLjI1LTExNS4xM3EtMS4yNS02LjMtMi44OC0xMi44Niw0LjU2LTEuMzUsOC45My0yLjc5YzU1LTE4LjI3LDkwLjgzLTQ3LjgxLDkwLjgzLTc4QzQ5NiwyMjYuNjIsNDYyLjUsMTk4LjYxLDQxMC42NiwxODAuNzJabS0xMjktODEuMDhjMzUuNDMtMzAuOTEsNjguNTUtNDMuMTEsODMuNjUtMzQuMzloMGMxNi4wNyw5LjI5LDIyLjMyLDQ2Ljc1LDEyLjIyLDk1Ljg4cS0xLDQuOC0yLjE2LDkuNTdhNDg3LjgzLDQ4Ny44MywwLDAsMC02NC4xOC0xMC4xNiw0ODEuMjcsNDgxLjI3LDAsMCwwLTQwLjU3LTUwLjc1UTI3NiwxMDQuNTcsMjgxLjY0LDk5LjY0Wk0xNTcuNzMsMjgwLjI1cTYuNTEsMTIuNiwxMy42MSwyNC44OSw3LjIzLDEyLjU0LDE1LjA3LDI0LjcxYTQzNS4yOCw0MzUuMjgsMCwwLDEtNDQuMjQtNy4xM0MxNDYuNDEsMzA5LDE1MS42MywyOTQuNzUsMTU3LjczLDI4MC4yNVptMC00OC4zM2MtNi0xNC4xOS0xMS4wOC0yOC4xNS0xNS4yNS00MS42MywxMy43LTMuMDcsMjguMy01LjU4LDQzLjUyLTcuNDhxLTcuNjUsMTEuOTQtMTQuNzIsMjQuMjNUMTU3LjcsMjMxLjkyWm0xMC45LDI0LjE3cTkuNDgtMTkuNzcsMjAuNDItMzguNzhoMHExMC45My0xOSwyMy4yNy0zNy4xM2MxNC4yOC0xLjA4LDI4LjkyLTEuNjUsNDMuNzEtMS42NXMyOS41Mi41Nyw0My43OSwxLjY2cTEyLjIxLDE4LjA5LDIzLjEzLDM3dDIwLjY5LDM4LjZRMzM0LDI3NS42MywzMjMsMjk0LjczaDBxLTEwLjkxLDE5LTIzLDM3LjI0Yy0xNC4yNSwxLTI5LDEuNTUtNDQsMS41NXMtMjkuNDctLjQ3LTQzLjQ2LTEuMzhxLTEyLjQzLTE4LjE5LTIzLjQ2LTM3LjI5VDE2OC42LDI1Ni4wOVpNMzQwLjc1LDMwNXE3LjI1LTEyLjU4LDEzLjkyLTI1LjQ5aDBhNDQwLjQxLDQ0MC40MSwwLDAsMSwxNi4xMiw0Mi4zMkE0MzQuNDQsNDM0LjQ0LDAsMCwxLDMyNiwzMjkuNDhRMzMzLjYyLDMxNy4zOSwzNDAuNzUsMzA1Wm0xMy43Mi03My4wN3EtNi42NC0xMi42NS0xMy44MS0yNWgwcS03LTEyLjE4LTE0LjU5LTI0LjA2YzE1LjMxLDEuOTQsMzAsNC41Miw0My43Nyw3LjY3QTQzOS44OSw0MzkuODksMCwwLDEsMzU0LjQ3LDIzMS45M1pNMjU2LjIzLDEyNC40OGgwYTQzOS43NSw0MzkuNzUsMCwwLDEsMjguMjUsMzQuMThxLTI4LjM1LTEuMzUtNTYuNzQsMEMyMzcuMDcsMTQ2LjMyLDI0Ni42MiwxMzQuODcsMjU2LjIzLDEyNC40OFpNMTQ1LjY2LDY1Ljg2YzE2LjA2LTkuMzIsNTEuNTcsNCw4OSwzNy4yNywyLjM5LDIuMTMsNC44LDQuMzYsNy4yLDYuNjdBNDkxLjM3LDQ5MS4zNywwLDAsMCwyMDEsMTYwLjUxYTQ5OS4xMiw0OTkuMTIsMCwwLDAtNjQuMDYsMTBxLTEuODMtNy4zNi0zLjMtMTQuODJoMEMxMjQuNTksMTA5LjQ2LDEzMC41OCw3NC42MSwxNDUuNjYsNjUuODZaTTEyMi4yNSwzMTcuNzFxLTYtMS43MS0xMS44NS0zLjcxYy0yMy40LTgtNDIuNzMtMTguNDQtNTYtMjkuODFDNDIuNTIsMjc0LDM2LjUsMjYzLjgzLDM2LjUsMjU1LjU5YzAtMTcuNTEsMjYuMDYtMzkuODUsNjkuNTItNTVxOC4xOS0yLjg1LDE2LjUyLTUuMjFhNDkzLjU0LDQ5My41NCwwLDAsMCwyMy40LDYwLjc1QTUwMi40Niw1MDIuNDYsMCwwLDAsMTIyLjI1LDMxNy43MVptMTExLjEzLDkzLjY3Yy0xOC42MywxNi4zMi0zNy4yOSwyNy44OS01My43NCwzMy43MmgwYy0xNC43OCw1LjIzLTI2LjU1LDUuMzgtMzMuNjYsMS4yNy0xNS4xNC04Ljc1LTIxLjQ0LTQyLjU0LTEyLjg1LTg3Ljg2cTEuNTMtOCwzLjUtMTZhNDgwLjg1LDQ4MC44NSwwLDAsMCw2NC42OSw5LjM5LDUwMS4yLDUwMS4yLDAsMCwwLDQxLjIsNTFDMjM5LjU0LDQwNS44MywyMzYuNDksNDA4LjY1LDIzMy4zOCw0MTEuMzhabTIzLjQyLTIzLjIyYy05LjcyLTEwLjUxLTE5LjQyLTIyLjE0LTI4Ljg4LTM0LjY0cTEzLjc5LjU0LDI4LjA4LjU0YzkuNzgsMCwxOS40Ni0uMjEsMjktLjY0QTQzOS4zMyw0MzkuMzMsMCwwLDEsMjU2LjgsMzg4LjE2Wm0xMjQuNTIsMjguNTljLTIuODYsMTUuNDQtOC42MSwyNS43NC0xNS43MiwyOS44Ni0xNS4xMyw4Ljc4LTQ3LjQ4LTIuNjMtODIuMzYtMzIuNzItNC0zLjQ0LTgtNy4xMy0xMi4wNy0xMWE0ODQuNTQsNDg0LjU0LDAsMCwwLDQwLjIzLTUxLjIsNDc3Ljg0LDQ3Ny44NCwwLDAsMCw2NS0xMC4wNXExLjQ3LDUuOTQsMi42LDExLjY0aDBDMzgzLjgxLDM3Ny41OCwzODQuNSwzOTkuNTYsMzgxLjMyLDQxNi43NVptMTcuNC0xMDIuNjRoMGMtMi42Mi44Ny01LjMyLDEuNzEtOC4wNiwyLjUzYTQ4My4yNiw0ODMuMjYsMCwwLDAtMjQuMzEtNjAuOTQsNDgxLjUyLDQ4MS41MiwwLDAsMCwyMy4zNi02MC4wNmM0LjkxLDEuNDMsOS42OCwyLjkzLDE0LjI3LDQuNTIsNDQuNDIsMTUuMzIsNzEuNTIsMzgsNzEuNTIsNTUuNDNDNDc1LjUsMjc0LjE5LDQ0Ni4yMywyOTguMzMsMzk4LjcyLDMxNC4xMVoiLz4KDTxwYXRoIGQ9Ik0yNTYsMjk4LjU1YTQzLDQzLDAsMSwwLTQyLjg2LTQzQTQyLjkxLDQyLjkxLDAsMCwwLDI1NiwyOTguNTVaIi8+Cg08L2c+Cg08L3N2Zz4=',
217
+ };
218
+ "
219
+ `;
220
+
221
+ exports[`cloudscape-website generator > should generate base files and structure > main.tsx 1`] = `
222
+ "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
223
+ SPDX-License-Identifier: Apache-2.0 */
224
+ import { NorthStarThemeProvider } from '@aws-northstar/ui';
225
+ import React from 'react';
226
+ import { createRoot } from 'react-dom/client';
227
+ import { BrowserRouter } from 'react-router-dom';
228
+ import { I18nProvider } from '@cloudscape-design/components/i18n';
229
+ import messages from '@cloudscape-design/components/i18n/messages/all.en';
230
+ import App from './layouts/App';
231
+
232
+ const root = document.getElementById('root');
233
+ root &&
234
+ createRoot(root).render(
235
+ <React.StrictMode>
236
+ <NorthStarThemeProvider>
237
+ <I18nProvider locale="en" messages={[messages]}>
238
+ <BrowserRouter>
239
+ <App />
240
+ </BrowserRouter>
241
+ </I18nProvider>
242
+ </NorthStarThemeProvider>
243
+ </React.StrictMode>
244
+ );
245
+ "
246
+ `;
247
+
248
+ exports[`cloudscape-website generator > should generate shared constructs > common/constructs-index.ts 1`] = `
249
+ "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
250
+ SPDX-License-Identifier: Apache-2.0 */
251
+ export * from './cloudfront-web-acl.js';
252
+ export * from './static-website.js';
253
+ "
254
+ `;
255
+
256
+ exports[`cloudscape-website generator > should generate shared constructs > static-website.ts 1`] = `
257
+ "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
258
+ SPDX-License-Identifier: Apache-2.0 */
259
+ import * as url from 'url';
260
+ import { PDKNag } from '@aws/pdk/pdk-nag';
261
+ import { CfnOutput, RemovalPolicy, Stack } from 'aws-cdk-lib';
262
+ import { Distribution, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront';
263
+ import { S3BucketOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
264
+ import {
265
+ BlockPublicAccess,
266
+ Bucket,
267
+ BucketEncryption,
268
+ IBucket,
269
+ ObjectOwnership,
270
+ } from 'aws-cdk-lib/aws-s3';
271
+ import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
272
+ import { NagSuppressions } from 'cdk-nag';
273
+ import { Construct } from 'constructs';
274
+ import { CloudfrontWebAcl } from './cloudfront-web-acl.js';
275
+ import { RuntimeConfig } from '../runtime-config/index.js';
276
+
277
+ const DEFAULT_RUNTIME_CONFIG_FILENAME = 'runtime-config.json';
278
+
279
+ /**
280
+ * Deploys a Static Website using by default a private S3 bucket as an origin and Cloudfront as the entrypoint.
281
+ *
282
+ * This construct configures a webAcl containing rules that are generally applicable to web applications. This
283
+ * provides protection against exploitation of a wide range of vulnerabilities, including some of the high risk
284
+ * and commonly occurring vulnerabilities described in OWASP publications such as OWASP Top 10.
285
+ *
286
+ */
287
+ export class StaticWebsite extends Construct {
288
+ public readonly websiteBucket: IBucket;
289
+ public readonly cloudFrontDistribution: Distribution;
290
+ public readonly bucketDeployment: BucketDeployment;
291
+
292
+ constructor(scope: Construct, id: string) {
293
+ super(scope, id);
294
+
295
+ this.node.setContext(
296
+ '@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy',
297
+ true
298
+ );
299
+
300
+ const accessLogsBucket = new Bucket(this, 'AccessLogsBucket', {
301
+ versioned: false,
302
+ enforceSSL: true,
303
+ autoDeleteObjects: true,
304
+ removalPolicy: RemovalPolicy.DESTROY,
305
+ encryption: BucketEncryption.S3_MANAGED,
306
+ objectOwnership: ObjectOwnership.OBJECT_WRITER,
307
+ publicReadAccess: false,
308
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
309
+ });
310
+
311
+ // S3 Bucket to hold website files
312
+ this.websiteBucket = new Bucket(this, 'WebsiteBucket', {
313
+ versioned: true,
314
+ enforceSSL: true,
315
+ autoDeleteObjects: true,
316
+ removalPolicy: RemovalPolicy.DESTROY,
317
+ encryption: BucketEncryption.S3_MANAGED,
318
+ objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
319
+ publicReadAccess: false,
320
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
321
+ serverAccessLogsPrefix: 'website-access-logs',
322
+ serverAccessLogsBucket: accessLogsBucket,
323
+ });
324
+
325
+ // Web ACL
326
+ const webAclArn = new CloudfrontWebAcl(this, 'WebsiteAcl').webAclArn;
327
+
328
+ // Cloudfront Distribution
329
+ const logBucket = new Bucket(this, 'DistributionLogBucket', {
330
+ enforceSSL: true,
331
+ autoDeleteObjects: true,
332
+ removalPolicy: RemovalPolicy.DESTROY,
333
+ encryption: BucketEncryption.S3_MANAGED,
334
+ objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED,
335
+ publicReadAccess: false,
336
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
337
+ serverAccessLogsPrefix: 'distribution-access-logs',
338
+ serverAccessLogsBucket: accessLogsBucket,
339
+ });
340
+
341
+ const defaultRootObject = 'index.html';
342
+ this.cloudFrontDistribution = new Distribution(
343
+ this,
344
+ 'CloudfrontDistribution',
345
+ {
346
+ webAclId: webAclArn,
347
+ enableLogging: true,
348
+ logBucket: logBucket,
349
+ defaultBehavior: {
350
+ origin: S3BucketOrigin.withOriginAccessControl(this.websiteBucket),
351
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
352
+ },
353
+ defaultRootObject,
354
+ errorResponses: [
355
+ {
356
+ httpStatus: 404, // We need to redirect "key not found errors" to index.html for single page apps
357
+ responseHttpStatus: 200,
358
+ responsePagePath: \`/\${defaultRootObject}\`,
359
+ },
360
+ ],
361
+ }
362
+ );
363
+
364
+ // Deploy Website
365
+ const runtimeConfig = RuntimeConfig.ensure(this).config;
366
+ this.bucketDeployment = new BucketDeployment(this, 'WebsiteDeployment', {
367
+ sources: [
368
+ Source.asset(
369
+ url.fileURLToPath(
370
+ new URL('../../../../../dist/test-app', import.meta.url)
371
+ )
372
+ ),
373
+ ...(Object.keys(runtimeConfig).length > 0
374
+ ? [Source.jsonData(DEFAULT_RUNTIME_CONFIG_FILENAME, runtimeConfig)]
375
+ : []),
376
+ ],
377
+ destinationBucket: this.websiteBucket,
378
+ // Files in the distribution's edge caches will be invalidated after files are uploaded to the destination bucket.
379
+ distribution: this.cloudFrontDistribution,
380
+ });
381
+
382
+ new CfnOutput(this, 'DistributionDomainName', {
383
+ value: this.cloudFrontDistribution.domainName,
384
+ });
385
+
386
+ this.suppressCDKNagViolations();
387
+ }
388
+
389
+ private suppressCDKNagViolations = () => {
390
+ const stack = Stack.of(this);
391
+
392
+ NagSuppressions.addResourceSuppressions(
393
+ this,
394
+ [
395
+ {
396
+ id: 'AwsPrototyping-CloudFrontDistributionGeoRestrictions',
397
+ reason:
398
+ 'Suppressed to allow unrestricted access. Not recommended in production.',
399
+ },
400
+ ],
401
+ true
402
+ );
403
+
404
+ [
405
+ 'AwsSolutions-CFR4',
406
+ 'AwsPrototyping-CloudFrontDistributionHttpsViewerNoOutdatedSSL',
407
+ ].forEach((RuleId) => {
408
+ NagSuppressions.addResourceSuppressions(this.cloudFrontDistribution, [
409
+ {
410
+ id: RuleId,
411
+ reason:
412
+ 'Certificate is not mandatory therefore the Cloudfront certificate will be used.',
413
+ },
414
+ ]);
415
+ });
416
+
417
+ ['AwsSolutions-L1', 'AwsPrototyping-LambdaLatestVersion'].forEach(
418
+ (RuleId) => {
419
+ NagSuppressions.addResourceSuppressions(
420
+ this,
421
+ [
422
+ {
423
+ id: RuleId,
424
+ reason:
425
+ 'Latest runtime cannot be configured. CDK will need to upgrade the BucketDeployment construct accordingly.',
426
+ },
427
+ ],
428
+ true
429
+ );
430
+ }
431
+ );
432
+
433
+ ['AwsSolutions-IAM5', 'AwsPrototyping-IAMNoWildcardPermissions'].forEach(
434
+ (RuleId) => {
435
+ NagSuppressions.addResourceSuppressions(
436
+ this,
437
+ [
438
+ {
439
+ id: RuleId,
440
+ reason:
441
+ 'All Policies have been scoped to a Bucket. Given Buckets can contain arbitrary content, wildcard resources with bucket scope are required.',
442
+ appliesTo: [
443
+ {
444
+ regex: '/^Action::s3:.*$/g',
445
+ },
446
+ {
447
+ regex: \`/^Resource::.*$/g\`,
448
+ },
449
+ ],
450
+ },
451
+ ],
452
+ true
453
+ );
454
+ }
455
+ );
456
+
457
+ ['AwsSolutions-IAM4', 'AwsPrototyping-IAMNoManagedPolicies'].forEach(
458
+ (RuleId) => {
459
+ NagSuppressions.addResourceSuppressions(
460
+ this,
461
+ [
462
+ {
463
+ id: RuleId,
464
+ reason:
465
+ 'Buckets can contain arbitrary content, therefore wildcard resources under a bucket are required.',
466
+ appliesTo: [
467
+ {
468
+ regex: \`/^Policy::arn:\${PDKNag.getStackPartitionRegex(
469
+ stack
470
+ )}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole$/g\`,
471
+ },
472
+ ],
473
+ },
474
+ ],
475
+ true
476
+ );
477
+ }
478
+ );
479
+
480
+ ['AwsSolutions-S1', 'AwsPrototyping-S3BucketLoggingEnabled'].forEach(
481
+ (RuleId) => {
482
+ NagSuppressions.addResourceSuppressions(
483
+ this,
484
+ [
485
+ {
486
+ id: RuleId,
487
+ reason: 'Access Log buckets should not have s3 bucket logging',
488
+ },
489
+ ],
490
+ true
491
+ );
492
+ }
493
+ );
494
+ };
495
+ }
496
+ "
497
+ `;
498
+
499
+ exports[`cloudscape-website generator > should handle custom directory option > custom-dir-main.tsx 1`] = `
500
+ "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
501
+ SPDX-License-Identifier: Apache-2.0 */
502
+ import { NorthStarThemeProvider } from '@aws-northstar/ui';
503
+ import React from 'react';
504
+ import { createRoot } from 'react-dom/client';
505
+ import { BrowserRouter } from 'react-router-dom';
506
+ import { I18nProvider } from '@cloudscape-design/components/i18n';
507
+ import messages from '@cloudscape-design/components/i18n/messages/all.en';
508
+ import App from './layouts/App';
509
+
510
+ const root = document.getElementById('root');
511
+ root &&
512
+ createRoot(root).render(
513
+ <React.StrictMode>
514
+ <NorthStarThemeProvider>
515
+ <I18nProvider locale="en" messages={[messages]}>
516
+ <BrowserRouter>
517
+ <App />
518
+ </BrowserRouter>
519
+ </I18nProvider>
520
+ </NorthStarThemeProvider>
521
+ </React.StrictMode>
522
+ );
523
+ "
524
+ `;
525
+
526
+ exports[`cloudscape-website generator > should handle npm scope prefix correctly > scoped-dependencies 1`] = `
527
+ {
528
+ "@aws-northstar/ui": "^1.1.13",
529
+ "@aws/pdk": "^0.25.7",
530
+ "@cloudscape-design/board-components": "^3.0.84",
531
+ "@cloudscape-design/components": "^3.0.823",
532
+ "aws-cdk-lib": "^2.166.0",
533
+ "cdk-nag": "^2.32.2",
534
+ "constructs": "^10.4.2",
535
+ "react": "18.3.1",
536
+ "react-dom": "18.3.1",
537
+ "react-router-dom": "^6.28.0",
538
+ }
539
+ `;
@@ -0,0 +1,4 @@
1
+ export default {
2
+ "applicationName": "<%= name %>",
3
+ "logo": "data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyBmaWxsPSIjMjQ4YmFlIiB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0cm9rZT0iIzI0OGJhZSI+Cg08ZyBpZD0iU1ZHUmVwb19iZ0NhcnJpZXIiIHN0cm9rZS13aWR0aD0iMCIvPgoNPGcgaWQ9IlNWR1JlcG9fdHJhY2VyQ2FycmllciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cg08ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+Cg08dGl0bGU+aW9uaWNvbnMtdjVfbG9nb3M8L3RpdGxlPgoNPHBhdGggZD0iTTQxMC42NiwxODAuNzJoMHEtNy42Ny0yLjYyLTE1LjQ1LTQuODgsMS4yOS01LjI1LDIuMzgtMTAuNTZjMTEuNy01Ni45LDQuMDUtMTAyLjc0LTIyLjA2LTExNy44My0yNS0xNC40OC02NiwuNjEtMTA3LjM2LDM2LjY5cS02LjEsNS4zNC0xMS45NSwxMS0zLjktMy43Ni04LTcuMzZjLTQzLjM1LTM4LjU4LTg2LjgtNTQuODMtMTEyLjg4LTM5LjY5LTI1LDE0LjUxLTMyLjQzLDU3LjYtMjEuOSwxMTEuNTNxMS41OCw4LDMuNTUsMTUuOTNjLTYuMTUsMS43NS0xMi4wOSwzLjYyLTE3Ljc3LDUuNkM0OC40NiwxOTguOSwxNiwyMjYuNzMsMTYsMjU1LjU5YzAsMjkuODIsMzQuODQsNTkuNzIsODcuNzcsNzcuODVxNi40NCwyLjE5LDEzLDQuMDdRMTE0LjY0LDM0NiwxMTMsMzU0LjY4Yy0xMCw1My0yLjIsOTUuMDcsMjIuNzUsMTA5LjQ5LDI1Ljc3LDE0Ljg5LDY5LS40MSwxMTEuMTQtMzcuMzFxNS00LjM4LDEwLTkuMjUsNi4zMiw2LjExLDEzLDExLjg2YzQwLjgsMzUuMTgsODEuMDksNDkuMzksMTA2LDM0LjkzLDI1Ljc1LTE0Ljk0LDM0LjEyLTYwLjE0LDIzLjI1LTExNS4xM3EtMS4yNS02LjMtMi44OC0xMi44Niw0LjU2LTEuMzUsOC45My0yLjc5YzU1LTE4LjI3LDkwLjgzLTQ3LjgxLDkwLjgzLTc4QzQ5NiwyMjYuNjIsNDYyLjUsMTk4LjYxLDQxMC42NiwxODAuNzJabS0xMjktODEuMDhjMzUuNDMtMzAuOTEsNjguNTUtNDMuMTEsODMuNjUtMzQuMzloMGMxNi4wNyw5LjI5LDIyLjMyLDQ2Ljc1LDEyLjIyLDk1Ljg4cS0xLDQuOC0yLjE2LDkuNTdhNDg3LjgzLDQ4Ny44MywwLDAsMC02NC4xOC0xMC4xNiw0ODEuMjcsNDgxLjI3LDAsMCwwLTQwLjU3LTUwLjc1UTI3NiwxMDQuNTcsMjgxLjY0LDk5LjY0Wk0xNTcuNzMsMjgwLjI1cTYuNTEsMTIuNiwxMy42MSwyNC44OSw3LjIzLDEyLjU0LDE1LjA3LDI0LjcxYTQzNS4yOCw0MzUuMjgsMCwwLDEtNDQuMjQtNy4xM0MxNDYuNDEsMzA5LDE1MS42MywyOTQuNzUsMTU3LjczLDI4MC4yNVptMC00OC4zM2MtNi0xNC4xOS0xMS4wOC0yOC4xNS0xNS4yNS00MS42MywxMy43LTMuMDcsMjguMy01LjU4LDQzLjUyLTcuNDhxLTcuNjUsMTEuOTQtMTQuNzIsMjQuMjNUMTU3LjcsMjMxLjkyWm0xMC45LDI0LjE3cTkuNDgtMTkuNzcsMjAuNDItMzguNzhoMHExMC45My0xOSwyMy4yNy0zNy4xM2MxNC4yOC0xLjA4LDI4LjkyLTEuNjUsNDMuNzEtMS42NXMyOS41Mi41Nyw0My43OSwxLjY2cTEyLjIxLDE4LjA5LDIzLjEzLDM3dDIwLjY5LDM4LjZRMzM0LDI3NS42MywzMjMsMjk0LjczaDBxLTEwLjkxLDE5LTIzLDM3LjI0Yy0xNC4yNSwxLTI5LDEuNTUtNDQsMS41NXMtMjkuNDctLjQ3LTQzLjQ2LTEuMzhxLTEyLjQzLTE4LjE5LTIzLjQ2LTM3LjI5VDE2OC42LDI1Ni4wOVpNMzQwLjc1LDMwNXE3LjI1LTEyLjU4LDEzLjkyLTI1LjQ5aDBhNDQwLjQxLDQ0MC40MSwwLDAsMSwxNi4xMiw0Mi4zMkE0MzQuNDQsNDM0LjQ0LDAsMCwxLDMyNiwzMjkuNDhRMzMzLjYyLDMxNy4zOSwzNDAuNzUsMzA1Wm0xMy43Mi03My4wN3EtNi42NC0xMi42NS0xMy44MS0yNWgwcS03LTEyLjE4LTE0LjU5LTI0LjA2YzE1LjMxLDEuOTQsMzAsNC41Miw0My43Nyw3LjY3QTQzOS44OSw0MzkuODksMCwwLDEsMzU0LjQ3LDIzMS45M1pNMjU2LjIzLDEyNC40OGgwYTQzOS43NSw0MzkuNzUsMCwwLDEsMjguMjUsMzQuMThxLTI4LjM1LTEuMzUtNTYuNzQsMEMyMzcuMDcsMTQ2LjMyLDI0Ni42MiwxMzQuODcsMjU2LjIzLDEyNC40OFpNMTQ1LjY2LDY1Ljg2YzE2LjA2LTkuMzIsNTEuNTcsNCw4OSwzNy4yNywyLjM5LDIuMTMsNC44LDQuMzYsNy4yLDYuNjdBNDkxLjM3LDQ5MS4zNywwLDAsMCwyMDEsMTYwLjUxYTQ5OS4xMiw0OTkuMTIsMCwwLDAtNjQuMDYsMTBxLTEuODMtNy4zNi0zLjMtMTQuODJoMEMxMjQuNTksMTA5LjQ2LDEzMC41OCw3NC42MSwxNDUuNjYsNjUuODZaTTEyMi4yNSwzMTcuNzFxLTYtMS43MS0xMS44NS0zLjcxYy0yMy40LTgtNDIuNzMtMTguNDQtNTYtMjkuODFDNDIuNTIsMjc0LDM2LjUsMjYzLjgzLDM2LjUsMjU1LjU5YzAtMTcuNTEsMjYuMDYtMzkuODUsNjkuNTItNTVxOC4xOS0yLjg1LDE2LjUyLTUuMjFhNDkzLjU0LDQ5My41NCwwLDAsMCwyMy40LDYwLjc1QTUwMi40Niw1MDIuNDYsMCwwLDAsMTIyLjI1LDMxNy43MVptMTExLjEzLDkzLjY3Yy0xOC42MywxNi4zMi0zNy4yOSwyNy44OS01My43NCwzMy43MmgwYy0xNC43OCw1LjIzLTI2LjU1LDUuMzgtMzMuNjYsMS4yNy0xNS4xNC04Ljc1LTIxLjQ0LTQyLjU0LTEyLjg1LTg3Ljg2cTEuNTMtOCwzLjUtMTZhNDgwLjg1LDQ4MC44NSwwLDAsMCw2NC42OSw5LjM5LDUwMS4yLDUwMS4yLDAsMCwwLDQxLjIsNTFDMjM5LjU0LDQwNS44MywyMzYuNDksNDA4LjY1LDIzMy4zOCw0MTEuMzhabTIzLjQyLTIzLjIyYy05LjcyLTEwLjUxLTE5LjQyLTIyLjE0LTI4Ljg4LTM0LjY0cTEzLjc5LjU0LDI4LjA4LjU0YzkuNzgsMCwxOS40Ni0uMjEsMjktLjY0QTQzOS4zMyw0MzkuMzMsMCwwLDEsMjU2LjgsMzg4LjE2Wm0xMjQuNTIsMjguNTljLTIuODYsMTUuNDQtOC42MSwyNS43NC0xNS43MiwyOS44Ni0xNS4xMyw4Ljc4LTQ3LjQ4LTIuNjMtODIuMzYtMzIuNzItNC0zLjQ0LTgtNy4xMy0xMi4wNy0xMWE0ODQuNTQsNDg0LjU0LDAsMCwwLDQwLjIzLTUxLjIsNDc3Ljg0LDQ3Ny44NCwwLDAsMCw2NS0xMC4wNXExLjQ3LDUuOTQsMi42LDExLjY0aDBDMzgzLjgxLDM3Ny41OCwzODQuNSwzOTkuNTYsMzgxLjMyLDQxNi43NVptMTcuNC0xMDIuNjRoMGMtMi42Mi44Ny01LjMyLDEuNzEtOC4wNiwyLjUzYTQ4My4yNiw0ODMuMjYsMCwwLDAtMjQuMzEtNjAuOTQsNDgxLjUyLDQ4MS41MiwwLDAsMCwyMy4zNi02MC4wNmM0LjkxLDEuNDMsOS42OCwyLjkzLDE0LjI3LDQuNTIsNDQuNDIsMTUuMzIsNzEuNTIsMzgsNzEuNTIsNTUuNDNDNDc1LjUsMjc0LjE5LDQ0Ni4yMywyOTguMzMsMzk4LjcyLDMxNC4xMVoiLz4KDTxwYXRoIGQ9Ik0yNTYsMjk4LjU1YTQzLDQzLDAsMSwwLTQyLjg2LTQzQTQyLjkxLDQyLjkxLDAsMCwwLDI1NiwyOTguNTVaIi8+Cg08L2c+Cg08L3N2Zz4="
4
+ }
@@ -0,0 +1,132 @@
1
+ /*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
2
+ SPDX-License-Identifier: Apache-2.0 */
3
+ import { useCognitoAuthContext } from "@aws-northstar/ui";
4
+ import getBreadcrumbs from "@aws-northstar/ui/components/AppLayout/utils/getBreadcrumbs";
5
+ import NavHeader from "@aws-northstar/ui/components/AppLayout/components/NavHeader";
6
+ import * as React from "react";
7
+ import { createContext, useCallback, useEffect, useState } from "react";
8
+ import { NavItems } from "./navitems";
9
+ import Config from "../../config";
10
+ import Routes from "../Routes";
11
+
12
+ import {
13
+ BreadcrumbGroup,
14
+ BreadcrumbGroupProps,
15
+ SideNavigation,
16
+ } from "@cloudscape-design/components";
17
+ import AppLayout, {
18
+ AppLayoutProps,
19
+ } from "@cloudscape-design/components/app-layout";
20
+ import { useLocation, useNavigate } from "react-router-dom";
21
+
22
+ /**
23
+ * Context for updating/retrieving the AppLayout.
24
+ */
25
+ export const AppLayoutContext = createContext({
26
+ appLayoutProps: {},
27
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
28
+ setAppLayoutProps: (_: AppLayoutProps) => {},
29
+ });
30
+
31
+ /**
32
+ * Defines the App layout and contains logic for routing.
33
+ */
34
+ const App: React.FC = () => {
35
+ const [username, setUsername] = useState<string | undefined>();
36
+ const [email, setEmail] = useState<string | undefined>();
37
+ const { getAuthenticatedUser } = useCognitoAuthContext();
38
+
39
+ const navigate = useNavigate();
40
+ const [activeHref, setActiveHref] = useState("/");
41
+ const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
42
+ BreadcrumbGroupProps.Item[]
43
+ >([{ text: "/", href: "/" }]);
44
+ const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
45
+ const location = useLocation();
46
+
47
+ useEffect(() => {
48
+ const authUser = getAuthenticatedUser();
49
+ setUsername(authUser?.getUsername());
50
+
51
+ authUser?.getSession(() => {
52
+ authUser.getUserAttributes((_, attributes) => {
53
+ setEmail(attributes?.find((a) => a.Name === "email")?.Value);
54
+ });
55
+ });
56
+ }, [getAuthenticatedUser, setUsername, setEmail]);
57
+
58
+ const setAppLayoutPropsSafe = useCallback(
59
+ (props: AppLayoutProps) => {
60
+ JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
61
+ setAppLayoutProps(props);
62
+ },
63
+ [appLayoutProps],
64
+ );
65
+
66
+ useEffect(() => {
67
+ setActiveHref(location.pathname);
68
+ const breadcrumbs = getBreadcrumbs(location.pathname, location.search, "/");
69
+ setActiveBreadcrumbs(breadcrumbs);
70
+ }, [location]);
71
+
72
+ const onNavigate = useCallback(
73
+ (e: CustomEvent<{ href: string; external?: boolean }>) => {
74
+ if (!e.detail.external) {
75
+ e.preventDefault();
76
+ setAppLayoutPropsSafe({
77
+ contentType: undefined,
78
+ splitPanelOpen: false,
79
+ splitPanelSize: undefined,
80
+ splitPanelPreferences: undefined,
81
+ });
82
+ navigate(e.detail.href);
83
+ }
84
+ },
85
+ [navigate, setAppLayoutPropsSafe],
86
+ );
87
+
88
+ return (
89
+ <AppLayoutContext.Provider
90
+ value={{ appLayoutProps, setAppLayoutProps: setAppLayoutPropsSafe }}
91
+ >
92
+ <NavHeader
93
+ title={Config.applicationName}
94
+ logo={Config.logo}
95
+ user={
96
+ username
97
+ ? {
98
+ username,
99
+ email,
100
+ }
101
+ : undefined
102
+ }
103
+ onSignout={() =>
104
+ new Promise(() => {
105
+ getAuthenticatedUser()?.signOut();
106
+ window.location.href = "/";
107
+ })
108
+ }
109
+ />
110
+ <AppLayout
111
+ breadcrumbs={
112
+ <BreadcrumbGroup onFollow={onNavigate} items={activeBreadcrumbs} />
113
+ }
114
+ toolsHide
115
+ navigation={
116
+ <SideNavigation
117
+ header={{ text: Config.applicationName, href: "/" }}
118
+ activeHref={activeHref}
119
+ onFollow={onNavigate}
120
+ items={NavItems}
121
+ />
122
+ }
123
+ content={<Routes />}
124
+ splitPanelOpen={false}
125
+ splitPanelPreferences={{ position: 'bottom' }}
126
+ {...appLayoutProps}
127
+ />
128
+ </AppLayoutContext.Provider>
129
+ );
130
+ };
131
+
132
+ export default App;
@@ -0,0 +1,8 @@
1
+ import { SideNavigationProps } from "@cloudscape-design/components";
2
+
3
+ /**
4
+ * Define your Navigation Items here
5
+ */
6
+ export const NavItems: SideNavigationProps.Item[] = [
7
+ { text: "Home", type: "link", href: "/" }
8
+ ];