@aws/nx-plugin 0.1.6 → 0.2.1

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 (126) hide show
  1. package/LICENSE-THIRD-PARTY +1554 -3241
  2. package/generators.json +1 -13
  3. package/package.json +14 -14
  4. package/src/cloudscape-website/app/README.md +84 -48
  5. package/src/cloudscape-website/app/__snapshots__/generator.spec.ts.snap +168 -233
  6. package/src/cloudscape-website/app/files/app/README.md.template +44 -0
  7. package/src/cloudscape-website/app/files/app/src/layouts/App/index.tsx.template +40 -43
  8. package/src/cloudscape-website/app/files/app/src/layouts/App/navitems.ts.template +3 -3
  9. package/src/cloudscape-website/app/files/app/src/layouts/Routes/index.tsx.template +4 -6
  10. package/src/cloudscape-website/app/files/app/src/main.tsx.template +7 -10
  11. package/src/cloudscape-website/app/files/app/src/pages/Home/index.tsx.template +0 -2
  12. package/src/cloudscape-website/app/files/common/constructs/src/app/static-websites/__websiteNameKebabCase__.ts.template +13 -0
  13. package/src/cloudscape-website/app/files/common/constructs/src/{__websiteNameKebabCase__ → core}/static-website.ts.template +79 -144
  14. package/src/cloudscape-website/app/generator.js +90 -74
  15. package/src/cloudscape-website/app/generator.js.map +1 -1
  16. package/src/cloudscape-website/app/schema.d.ts +3 -5
  17. package/src/cloudscape-website/app/schema.json +1 -24
  18. package/src/cloudscape-website/cognito-auth/README.md +53 -32
  19. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +162 -124
  20. package/src/cloudscape-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +53 -39
  21. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +168 -0
  22. package/src/cloudscape-website/cognito-auth/generator.js +130 -47
  23. package/src/cloudscape-website/cognito-auth/generator.js.map +1 -1
  24. package/src/cloudscape-website/cognito-auth/schema.d.ts +1 -0
  25. package/src/cloudscape-website/cognito-auth/schema.json +7 -1
  26. package/src/cloudscape-website/runtime-config/__snapshots__/generator.spec.ts.snap +20 -15
  27. package/src/cloudscape-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +7 -10
  28. package/src/cloudscape-website/runtime-config/files/app/hooks/useRuntimeConfig.tsx.template +13 -0
  29. package/src/cloudscape-website/runtime-config/generator.js +4 -2
  30. package/src/cloudscape-website/runtime-config/generator.js.map +1 -1
  31. package/src/infra/app/README.md +71 -46
  32. package/src/infra/app/__snapshots__/generator.spec.ts.snap +184 -305
  33. package/src/infra/app/files/app/README.md.template +76 -0
  34. package/src/infra/app/files/app/src/main.ts.template +18 -0
  35. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/aws-prototyping.guard +1282 -0
  36. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/cfn-nag.guard +6839 -0
  37. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/hipaa-security.guard +2807 -0
  38. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/nist-csf.guard +2585 -0
  39. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/pci-dss-3-2-1.guard +2236 -0
  40. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-reliability-pillar.guard +885 -0
  41. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-security-pillar.guard +2205 -0
  42. package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +63 -0
  43. package/src/infra/app/generator.js +36 -7
  44. package/src/infra/app/generator.js.map +1 -1
  45. package/src/infra/app/schema.d.ts +10 -1
  46. package/src/infra/app/schema.json +16 -8
  47. package/src/trpc/backend/README.md +102 -80
  48. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +42 -19
  49. package/src/trpc/backend/files/backend/README.md.template +33 -0
  50. package/src/trpc/backend/files/common/constructs/src/app/trpc-apis/__apiNameKebabCase__.ts.template +18 -0
  51. package/src/trpc/backend/files/common/constructs/src/{__apiNameKebabCase__/index.ts.template → core/trpc-api.ts.template} +12 -16
  52. package/src/trpc/backend/files/schema/README.md.template +33 -0
  53. package/src/trpc/backend/generator.js +30 -44
  54. package/src/trpc/backend/generator.js.map +1 -1
  55. package/src/trpc/backend/schema.d.ts +3 -1
  56. package/src/trpc/backend/schema.json +8 -13
  57. package/src/trpc/react/README.md +46 -66
  58. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +104 -65
  59. package/src/trpc/react/files/src/components/TrpcClients/IsolatedTrpcProvider.tsx.template +75 -0
  60. package/src/trpc/react/files/src/components/TrpcClients/TrpcApis.tsx.template +1 -0
  61. package/src/trpc/react/files/src/components/TrpcClients/TrpcClientProviders.tsx.template +10 -0
  62. package/src/trpc/react/files/src/components/TrpcClients/index.tsx.template +5 -0
  63. package/src/trpc/react/files/src/hooks/useSigV4.tsx.template +38 -0
  64. package/src/trpc/react/files/src/hooks/use__apiNameClassName__.tsx.template +3 -0
  65. package/src/trpc/react/generator.js +124 -25
  66. package/src/trpc/react/generator.js.map +1 -1
  67. package/src/trpc/react/schema.json +2 -2
  68. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +47 -93
  69. package/src/ts/lib/eslint.d.ts +1 -2
  70. package/src/ts/lib/eslint.js +62 -21
  71. package/src/ts/lib/eslint.js.map +1 -1
  72. package/src/ts/lib/files/README.md.template +33 -0
  73. package/src/ts/lib/generator.js +44 -5
  74. package/src/ts/lib/generator.js.map +1 -1
  75. package/src/ts/lib/schema.d.ts +1 -4
  76. package/src/ts/lib/schema.json +2 -21
  77. package/src/ts/lib/ts-project-utils.js +3 -18
  78. package/src/ts/lib/ts-project-utils.js.map +1 -1
  79. package/src/ts/lib/vitest.js +12 -0
  80. package/src/ts/lib/vitest.js.map +1 -1
  81. package/src/utils/ast.d.ts +13 -0
  82. package/src/utils/ast.js +102 -0
  83. package/src/utils/ast.js.map +1 -0
  84. package/src/utils/files/common/constructs/src/app/index.ts.template +0 -0
  85. package/src/utils/files/common/constructs/src/{runtime-config → core}/runtime-config.ts.template +3 -5
  86. package/src/utils/files/common/constructs/src/index.ts.template +2 -1
  87. package/src/utils/files/common/readme/README.md.template +33 -0
  88. package/src/utils/files/common/types/src/runtime-config.ts.template +2 -13
  89. package/src/utils/format.d.ts +1 -1
  90. package/src/utils/format.js +2 -2
  91. package/src/utils/format.js.map +1 -1
  92. package/src/utils/names.d.ts +2 -0
  93. package/src/utils/names.js +27 -0
  94. package/src/utils/names.js.map +1 -0
  95. package/src/utils/npm-scope.js.map +1 -1
  96. package/src/utils/paths.js.map +1 -1
  97. package/src/utils/shared-constructs.js +37 -4
  98. package/src/utils/shared-constructs.js.map +1 -1
  99. package/src/utils/test.d.ts +2 -0
  100. package/src/utils/test.js +19 -0
  101. package/src/utils/test.js.map +1 -0
  102. package/src/utils/versions.d.ts +15 -9
  103. package/src/utils/versions.js +14 -8
  104. package/src/utils/versions.js.map +1 -1
  105. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/cloudfront-web-acl.ts.template +0 -317
  106. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/index.ts.template +0 -4
  107. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/webacl_event_handler/index.ts.template +0 -301
  108. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/index.ts.template +0 -4
  109. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/user-identity.ts.template +0 -66
  110. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/userpool-with-mfa.ts.template +0 -70
  111. package/src/gitlab/generator.d.ts +0 -8
  112. package/src/gitlab/generator.js +0 -16
  113. package/src/gitlab/generator.js.map +0 -1
  114. package/src/gitlab/schema.d.ts +0 -9
  115. package/src/gitlab/schema.json +0 -52
  116. package/src/infra/app/files/src/main.ts.template +0 -37
  117. package/src/trpc/react/files/src/components/TRPCClientProvider/index.tsx.template +0 -34
  118. package/src/trpc/react/files/src/hooks/useTrpc.tsx.template +0 -5
  119. package/src/ts/cjs-to-esm/generator.d.ts +0 -12
  120. package/src/ts/cjs-to-esm/generator.js +0 -189
  121. package/src/ts/cjs-to-esm/generator.js.map +0 -1
  122. package/src/ts/cjs-to-esm/schema.d.ts +0 -9
  123. package/src/ts/cjs-to-esm/schema.json +0 -28
  124. /package/src/infra/app/files/{cdk.json → app/cdk.json} +0 -0
  125. /package/src/infra/app/files/{src → app/src}/stacks/application-stack.ts.template +0 -0
  126. /package/src/utils/files/common/constructs/src/{runtime-config → core}/index.ts.template +0 -0
@@ -3,17 +3,8 @@
3
3
  exports[`cloudscape-website generator > should configure TypeScript correctly > tsconfig.json 1`] = `
4
4
  {
5
5
  "compilerOptions": {
6
- "allowJs": false,
7
- "allowSyntheticDefaultImports": true,
8
- "esModuleInterop": false,
9
- "jsx": "react-jsx",
10
6
  "module": "Preserve",
11
7
  "moduleResolution": "Bundler",
12
- "strict": true,
13
- "types": [
14
- "vite/client",
15
- "vitest",
16
- ],
17
8
  },
18
9
  "extends": "../tsconfig.base.json",
19
10
  "files": [],
@@ -75,12 +66,7 @@ export default defineConfig({
75
66
  `;
76
67
 
77
68
  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';
69
+ "import * as React from 'react';
84
70
  import { createContext, useCallback, useEffect, useState } from 'react';
85
71
  import { NavItems } from './navitems';
86
72
  import Config from '../../config';
@@ -90,11 +76,42 @@ import {
90
76
  BreadcrumbGroup,
91
77
  BreadcrumbGroupProps,
92
78
  SideNavigation,
79
+ TopNavigation,
93
80
  } from '@cloudscape-design/components';
94
81
  import AppLayout, {
95
82
  AppLayoutProps,
96
83
  } from '@cloudscape-design/components/app-layout';
97
- import { useLocation, useNavigate } from 'react-router-dom';
84
+ import { matchPath, useLocation, useNavigate } from 'react-router-dom';
85
+
86
+ const getBreadcrumbs = (
87
+ pathName: string,
88
+ search: string,
89
+ defaultBreadcrumb: string,
90
+ availableRoutes?: string[],
91
+ ) => {
92
+ const segments = [
93
+ defaultBreadcrumb,
94
+ ...pathName.split('/').filter((segment) => segment !== ''),
95
+ ];
96
+
97
+ return segments.map((segment, i) => {
98
+ const href =
99
+ i === 0
100
+ ? '/'
101
+ : \`/\${segments
102
+ .slice(1, i + 1)
103
+ .join('/')
104
+ .replace('//', '/')}\`;
105
+
106
+ const matched =
107
+ !availableRoutes || availableRoutes.find((r) => matchPath(r, href));
108
+
109
+ return {
110
+ href: matched ? \`\${href}\${search}\` : '#',
111
+ text: segment,
112
+ };
113
+ });
114
+ };
98
115
 
99
116
  /**
100
117
  * Context for updating/retrieving the AppLayout.
@@ -109,10 +126,6 @@ export const AppLayoutContext = createContext({
109
126
  * Defines the App layout and contains logic for routing.
110
127
  */
111
128
  const App: React.FC = () => {
112
- const [username, setUsername] = useState<string | undefined>();
113
- const [email, setEmail] = useState<string | undefined>();
114
- const { getAuthenticatedUser } = useCognitoAuthContext();
115
-
116
129
  const navigate = useNavigate();
117
130
  const [activeHref, setActiveHref] = useState('/');
118
131
  const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
@@ -121,23 +134,12 @@ const App: React.FC = () => {
121
134
  const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
122
135
  const location = useLocation();
123
136
 
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
137
  const setAppLayoutPropsSafe = useCallback(
136
138
  (props: AppLayoutProps) => {
137
139
  JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
138
140
  setAppLayoutProps(props);
139
141
  },
140
- [appLayoutProps]
142
+ [appLayoutProps],
141
143
  );
142
144
 
143
145
  useEffect(() => {
@@ -152,37 +154,25 @@ const App: React.FC = () => {
152
154
  e.preventDefault();
153
155
  setAppLayoutPropsSafe({
154
156
  contentType: undefined,
155
- splitPanelOpen: false,
156
- splitPanelSize: undefined,
157
- splitPanelPreferences: undefined,
158
157
  });
159
158
  navigate(e.detail.href);
160
159
  }
161
160
  },
162
- [navigate, setAppLayoutPropsSafe]
161
+ [navigate, setAppLayoutPropsSafe],
163
162
  );
164
163
 
165
164
  return (
166
165
  <AppLayoutContext.Provider
167
166
  value={{ appLayoutProps, setAppLayoutProps: setAppLayoutPropsSafe }}
168
167
  >
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
- }
168
+ <TopNavigation
169
+ identity={{
170
+ href: '/',
171
+ title: Config.applicationName,
172
+ logo: {
173
+ src: Config.logo,
174
+ },
175
+ }}
186
176
  />
187
177
  <AppLayout
188
178
  breadcrumbs={
@@ -198,8 +188,6 @@ const App: React.FC = () => {
198
188
  />
199
189
  }
200
190
  content={<Routes />}
201
- splitPanelOpen={false}
202
- splitPanelPreferences={{ position: 'bottom' }}
203
191
  {...appLayoutProps}
204
192
  />
205
193
  </AppLayoutContext.Provider>
@@ -219,46 +207,42 @@ exports[`cloudscape-website generator > should generate base files and structure
219
207
  `;
220
208
 
221
209
  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';
210
+ "import React from 'react';
226
211
  import { createRoot } from 'react-dom/client';
227
212
  import { BrowserRouter } from 'react-router-dom';
228
213
  import { I18nProvider } from '@cloudscape-design/components/i18n';
229
214
  import messages from '@cloudscape-design/components/i18n/messages/all.en';
230
215
  import App from './layouts/App';
231
216
 
217
+ import '@cloudscape-design/global-styles/index.css';
218
+
232
219
  const root = document.getElementById('root');
233
220
  root &&
234
221
  createRoot(root).render(
235
222
  <React.StrictMode>
236
- <NorthStarThemeProvider>
237
- <I18nProvider locale="en" messages={[messages]}>
238
- <BrowserRouter>
239
- <App />
240
- </BrowserRouter>
241
- </I18nProvider>
242
- </NorthStarThemeProvider>
243
- </React.StrictMode>
223
+ <I18nProvider locale="en" messages={[messages]}>
224
+ <BrowserRouter>
225
+ <App />
226
+ </BrowserRouter>
227
+ </I18nProvider>
228
+ </React.StrictMode>,
244
229
  );
245
230
  "
246
231
  `;
247
232
 
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';
233
+ exports[`cloudscape-website generator > should generate shared constructs > common/constructs-app-index.ts 1`] = `
234
+ "export * from './test-app.js';
253
235
  "
254
236
  `;
255
237
 
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 { CfnJson, CfnOutput, Lazy, RemovalPolicy, Stack, Token } from 'aws-cdk-lib';
238
+ exports[`cloudscape-website generator > should generate shared constructs > common/constructs-core-index.ts 1`] = `
239
+ "export * from './static-website.js';
240
+ export * from './runtime-config.js';
241
+ "
242
+ `;
243
+
244
+ exports[`cloudscape-website generator > should generate shared constructs > common/constructs-core-static-website.ts 1`] = `
245
+ "import { CfnJson, CfnOutput, RemovalPolicy, Stack, Token } from 'aws-cdk-lib';
262
246
  import { Distribution, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront';
263
247
  import { S3BucketOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
264
248
  import {
@@ -269,13 +253,16 @@ import {
269
253
  ObjectOwnership,
270
254
  } from 'aws-cdk-lib/aws-s3';
271
255
  import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
272
- import { NagSuppressions } from 'cdk-nag';
273
256
  import { Construct } from 'constructs';
274
- import { CloudfrontWebAcl } from './cloudfront-web-acl.js';
275
- import { RuntimeConfig } from '../runtime-config/index.js';
276
-
257
+ import { RuntimeConfig } from './runtime-config.js';
258
+ import { Key } from 'aws-cdk-lib/aws-kms';
259
+ import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2';
277
260
  const DEFAULT_RUNTIME_CONFIG_FILENAME = 'runtime-config.json';
278
261
 
262
+ export interface StaticWebsiteProps {
263
+ readonly websiteFilePath: string;
264
+ }
265
+
279
266
  /**
280
267
  * Deploys a Static Website using by default a private S3 bucket as an origin and Cloudfront as the entrypoint.
281
268
  *
@@ -289,61 +276,68 @@ export class StaticWebsite extends Construct {
289
276
  public readonly cloudFrontDistribution: Distribution;
290
277
  public readonly bucketDeployment: BucketDeployment;
291
278
 
292
- constructor(scope: Construct, id: string) {
279
+ constructor(
280
+ scope: Construct,
281
+ id: string,
282
+ { websiteFilePath }: StaticWebsiteProps,
283
+ ) {
293
284
  super(scope, id);
294
-
295
285
  this.node.setContext(
296
286
  '@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy',
297
- true
287
+ true,
298
288
  );
299
289
 
290
+ const websiteKey = new Key(this, 'WebsiteKey', {
291
+ enableKeyRotation: true,
292
+ });
293
+
300
294
  const accessLogsBucket = new Bucket(this, 'AccessLogsBucket', {
301
295
  versioned: false,
302
296
  enforceSSL: true,
303
297
  autoDeleteObjects: true,
304
298
  removalPolicy: RemovalPolicy.DESTROY,
305
- encryption: BucketEncryption.S3_MANAGED,
299
+ encryption: BucketEncryption.KMS,
300
+ encryptionKey: websiteKey,
306
301
  objectOwnership: ObjectOwnership.OBJECT_WRITER,
307
302
  publicReadAccess: false,
308
303
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
309
304
  });
310
-
311
305
  // S3 Bucket to hold website files
312
306
  this.websiteBucket = new Bucket(this, 'WebsiteBucket', {
313
307
  versioned: true,
314
308
  enforceSSL: true,
315
309
  autoDeleteObjects: true,
316
310
  removalPolicy: RemovalPolicy.DESTROY,
317
- encryption: BucketEncryption.S3_MANAGED,
311
+ encryption: BucketEncryption.KMS,
312
+ encryptionKey: websiteKey,
318
313
  objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
319
314
  publicReadAccess: false,
320
315
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
321
316
  serverAccessLogsPrefix: 'website-access-logs',
322
317
  serverAccessLogsBucket: accessLogsBucket,
323
318
  });
324
-
325
319
  // Web ACL
326
- const webAclArn = new CloudfrontWebAcl(this, 'WebsiteAcl').webAclArn;
320
+ const wafStack = new CloudfrontWebAcl(this, 'waf');
327
321
 
328
322
  // Cloudfront Distribution
329
323
  const logBucket = new Bucket(this, 'DistributionLogBucket', {
330
324
  enforceSSL: true,
331
325
  autoDeleteObjects: true,
332
326
  removalPolicy: RemovalPolicy.DESTROY,
333
- encryption: BucketEncryption.S3_MANAGED,
327
+ encryption: BucketEncryption.KMS,
328
+ encryptionKey: websiteKey,
334
329
  objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED,
335
330
  publicReadAccess: false,
336
331
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
337
332
  serverAccessLogsPrefix: 'distribution-access-logs',
338
333
  serverAccessLogsBucket: accessLogsBucket,
339
334
  });
340
-
341
335
  const defaultRootObject = 'index.html';
342
336
  this.cloudFrontDistribution = new Distribution(
343
337
  this,
344
338
  'CloudfrontDistribution',
345
339
  {
346
- webAclId: webAclArn,
340
+ webAclId: wafStack.wafArn,
347
341
  enableLogging: true,
348
342
  logBucket: logBucket,
349
343
  defaultBehavior: {
@@ -357,48 +351,42 @@ export class StaticWebsite extends Construct {
357
351
  responseHttpStatus: 200,
358
352
  responsePagePath: \`/\${defaultRootObject}\`,
359
353
  },
354
+ {
355
+ httpStatus: 403, // We need to redirect reloads from paths (e.g. /foo/bar) to index.html for single page apps
356
+ responseHttpStatus: 200,
357
+ responsePagePath: \`/\${defaultRootObject}\`,
358
+ },
360
359
  ],
361
- }
360
+ },
362
361
  );
363
-
364
362
  // Deploy Website
365
- const runtimeConfig = RuntimeConfig.ensure(this).config;
366
363
  this.bucketDeployment = new BucketDeployment(this, 'WebsiteDeployment', {
367
364
  sources: [
368
- Source.asset(
369
- url.fileURLToPath(
370
- new URL('../../../../../dist/test-app', import.meta.url)
371
- )
365
+ Source.asset(websiteFilePath),
366
+ Source.jsonData(
367
+ DEFAULT_RUNTIME_CONFIG_FILENAME,
368
+ this.resolveTokens(RuntimeConfig.ensure(this).config),
372
369
  ),
373
- ...(Object.keys(runtimeConfig).length > 0
374
- ? [Source.jsonData(DEFAULT_RUNTIME_CONFIG_FILENAME, this.lazilyRender(runtimeConfig))]
375
- : []),
376
370
  ],
377
371
  destinationBucket: this.websiteBucket,
378
372
  // Files in the distribution's edge caches will be invalidated after files are uploaded to the destination bucket.
379
373
  distribution: this.cloudFrontDistribution,
380
374
  });
381
-
382
375
  new CfnOutput(this, 'DistributionDomainName', {
383
376
  value: this.cloudFrontDistribution.domainName,
384
377
  });
385
-
386
- this.suppressCDKNagViolations();
387
378
  }
388
-
389
- private lazilyRender = (payload: any) => Lazy.any({
390
- produce: () => this.resolveTokens(payload),
391
- });
392
-
393
379
  private resolveTokens = (payload: any) => {
394
380
  const _payload: Record<string, any> = {};
395
-
396
381
  Object.entries(payload).forEach(([key, value]) => {
397
- if (Token.isUnresolved(value) || (typeof value === "string" && value.endsWith("}}"))) {
382
+ if (
383
+ Token.isUnresolved(value) ||
384
+ (typeof value === 'string' && value.endsWith('}}'))
385
+ ) {
398
386
  _payload[key] = new CfnJson(this, \`ResolveToken-\${key}\`, {
399
- value,
387
+ value,
400
388
  }).value;
401
- } else if (typeof value === "object") {
389
+ } else if (typeof value === 'object') {
402
390
  _payload[key] = this.resolveTokens(value);
403
391
  } else if (Array.isArray(value)) {
404
392
  _payload[key] = value.map((v) => this.resolveTokens(v));
@@ -406,158 +394,105 @@ export class StaticWebsite extends Construct {
406
394
  _payload[key] = value;
407
395
  }
408
396
  });
409
-
410
397
  return _payload;
411
- }
398
+ };
399
+ }
412
400
 
413
- private suppressCDKNagViolations = () => {
414
- const stack = Stack.of(this);
401
+ export class CloudfrontWebAcl extends Stack {
402
+ public readonly wafArn;
403
+ constructor(scope: Construct, id: string) {
404
+ super(scope, id, {
405
+ env: {
406
+ region: 'us-east-1',
407
+ account: Stack.of(scope).account,
408
+ },
409
+ crossRegionReferences: true,
410
+ });
415
411
 
416
- NagSuppressions.addResourceSuppressions(
417
- this,
418
- [
412
+ this.wafArn = new CfnWebACL(this, 'WebAcl', {
413
+ defaultAction: { allow: {} },
414
+ scope: 'CLOUDFRONT',
415
+ visibilityConfig: {
416
+ cloudWatchMetricsEnabled: true,
417
+ metricName: id,
418
+ sampledRequestsEnabled: true,
419
+ },
420
+ rules: [
419
421
  {
420
- id: 'AwsPrototyping-CloudFrontDistributionGeoRestrictions',
421
- reason:
422
- 'Suppressed to allow unrestricted access. Not recommended in production.',
422
+ name: 'CRSRule',
423
+ priority: 0,
424
+ statement: {
425
+ managedRuleGroupStatement: {
426
+ name: 'AWSManagedRulesCommonRuleSet',
427
+ vendorName: 'AWS',
428
+ },
429
+ },
430
+ visibilityConfig: {
431
+ cloudWatchMetricsEnabled: true,
432
+ metricName: 'MetricForWebACLCDK-CRS',
433
+ sampledRequestsEnabled: true,
434
+ },
435
+ overrideAction: {
436
+ none: {},
437
+ },
423
438
  },
424
439
  ],
425
- true
426
- );
427
-
428
- [
429
- 'AwsSolutions-CFR4',
430
- 'AwsPrototyping-CloudFrontDistributionHttpsViewerNoOutdatedSSL',
431
- ].forEach((RuleId) => {
432
- NagSuppressions.addResourceSuppressions(this.cloudFrontDistribution, [
433
- {
434
- id: RuleId,
435
- reason:
436
- 'Certificate is not mandatory therefore the Cloudfront certificate will be used.',
437
- },
438
- ]);
439
- });
440
-
441
- ['AwsSolutions-L1', 'AwsPrototyping-LambdaLatestVersion'].forEach(
442
- (RuleId) => {
443
- NagSuppressions.addResourceSuppressions(
444
- this,
445
- [
446
- {
447
- id: RuleId,
448
- reason:
449
- 'Latest runtime cannot be configured. CDK will need to upgrade the BucketDeployment construct accordingly.',
450
- },
451
- ],
452
- true
453
- );
454
- }
455
- );
456
-
457
- ['AwsSolutions-IAM5', 'AwsPrototyping-IAMNoWildcardPermissions'].forEach(
458
- (RuleId) => {
459
- NagSuppressions.addResourceSuppressions(
460
- this,
461
- [
462
- {
463
- id: RuleId,
464
- reason:
465
- 'All Policies have been scoped to a Bucket. Given Buckets can contain arbitrary content, wildcard resources with bucket scope are required.',
466
- appliesTo: [
467
- {
468
- regex: '/^Action::s3:.*$/g',
469
- },
470
- {
471
- regex: \`/^Resource::.*$/g\`,
472
- },
473
- ],
474
- },
475
- ],
476
- true
477
- );
478
- }
479
- );
440
+ }).attrArn;
441
+ }
442
+ }
443
+ "
444
+ `;
480
445
 
481
- ['AwsSolutions-IAM4', 'AwsPrototyping-IAMNoManagedPolicies'].forEach(
482
- (RuleId) => {
483
- NagSuppressions.addResourceSuppressions(
484
- this,
485
- [
486
- {
487
- id: RuleId,
488
- reason:
489
- 'Buckets can contain arbitrary content, therefore wildcard resources under a bucket are required.',
490
- appliesTo: [
491
- {
492
- regex: \`/^Policy::arn:\${PDKNag.getStackPartitionRegex(
493
- stack
494
- )}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole$/g\`,
495
- },
496
- ],
497
- },
498
- ],
499
- true
500
- );
501
- }
502
- );
446
+ exports[`cloudscape-website generator > should generate shared constructs > test-app.ts 1`] = `
447
+ "import * as url from 'url';
448
+ import { Construct } from 'constructs';
449
+ import { StaticWebsite } from '../../core/index.js';
503
450
 
504
- ['AwsSolutions-S1', 'AwsPrototyping-S3BucketLoggingEnabled'].forEach(
505
- (RuleId) => {
506
- NagSuppressions.addResourceSuppressions(
507
- this,
508
- [
509
- {
510
- id: RuleId,
511
- reason: 'Access Log buckets should not have s3 bucket logging',
512
- },
513
- ],
514
- true
515
- );
516
- }
517
- );
518
- };
451
+ export class TestApp extends StaticWebsite {
452
+ constructor(scope: Construct, id: string) {
453
+ super(scope, id, {
454
+ websiteFilePath: url.fileURLToPath(
455
+ new URL('../../../../../../dist/test-app', import.meta.url),
456
+ ),
457
+ });
458
+ }
519
459
  }
520
460
  "
521
461
  `;
522
462
 
523
463
  exports[`cloudscape-website generator > should handle custom directory option > custom-dir-main.tsx 1`] = `
524
- "/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
525
- SPDX-License-Identifier: Apache-2.0 */
526
- import { NorthStarThemeProvider } from '@aws-northstar/ui';
527
- import React from 'react';
464
+ "import React from 'react';
528
465
  import { createRoot } from 'react-dom/client';
529
466
  import { BrowserRouter } from 'react-router-dom';
530
467
  import { I18nProvider } from '@cloudscape-design/components/i18n';
531
468
  import messages from '@cloudscape-design/components/i18n/messages/all.en';
532
469
  import App from './layouts/App';
533
470
 
471
+ import '@cloudscape-design/global-styles/index.css';
472
+
534
473
  const root = document.getElementById('root');
535
474
  root &&
536
475
  createRoot(root).render(
537
476
  <React.StrictMode>
538
- <NorthStarThemeProvider>
539
- <I18nProvider locale="en" messages={[messages]}>
540
- <BrowserRouter>
541
- <App />
542
- </BrowserRouter>
543
- </I18nProvider>
544
- </NorthStarThemeProvider>
545
- </React.StrictMode>
477
+ <I18nProvider locale="en" messages={[messages]}>
478
+ <BrowserRouter>
479
+ <App />
480
+ </BrowserRouter>
481
+ </I18nProvider>
482
+ </React.StrictMode>,
546
483
  );
547
484
  "
548
485
  `;
549
486
 
550
487
  exports[`cloudscape-website generator > should handle npm scope prefix correctly > scoped-dependencies 1`] = `
551
488
  {
552
- "@aws-northstar/ui": "^1.1.13",
553
- "@aws/pdk": "^0.25.7",
554
489
  "@cloudscape-design/board-components": "^3.0.84",
555
490
  "@cloudscape-design/components": "^3.0.823",
491
+ "@cloudscape-design/global-styles": "^1.0.34",
556
492
  "aws-cdk-lib": "^2.166.0",
557
- "cdk-nag": "^2.32.2",
558
493
  "constructs": "^10.4.2",
559
494
  "react": "18.3.1",
560
495
  "react-dom": "18.3.1",
561
- "react-router-dom": "^6.28.0",
496
+ "react-router-dom": "^7.1.1",
562
497
  }
563
498
  `;
@@ -0,0 +1,44 @@
1
+ # <%= fullyQualifiedName %>
2
+ This library was generated with [@aws/nx-plugin](https://github.com/awslabs/nx-plugin-for-aws/).
3
+
4
+ ## Building
5
+
6
+ Run `<%= pkgMgrCmd %> nx build <%= fullyQualifiedName %> [--skip-nx-cache]` to build the application.
7
+
8
+ ## Run dev server
9
+
10
+ Run `<%= pkgMgrCmd %> nx serve <%= fullyQualifiedName %>`
11
+
12
+ ## Running unit tests
13
+
14
+ Run `<%= pkgMgrCmd %> nx test <%= fullyQualifiedName %>` to execute the unit tests via Vitest.
15
+
16
+ ### Updating snapshots
17
+
18
+ To update snapshots, run the following command:
19
+
20
+ `<%= pkgMgrCmd %> nx test <%= fullyQualifiedName %> --configuration=update-snapshot`
21
+
22
+ ## Run lint
23
+
24
+ Run `<%= pkgMgrCmd %> nx lint <%= fullyQualifiedName %>`
25
+
26
+ ### Fixable issues
27
+
28
+ You can also automatically fix some lint errors by running the following command:
29
+
30
+ `<%= pkgMgrCmd %> nx lint <%= fullyQualifiedName %> --configuration=fix`
31
+
32
+ ### Runtime config
33
+
34
+ In order to integrate with cognito or trpc backends, you need to have a `runtime-config.json` file in your `/public` website directory. You can fetch this is follows:
35
+
36
+ `AWS_REGION=ap-southeast-2 CDK_APP_DIR=./dist/packages/infra/cdk.out nx run <%= fullyQualifiedName %>:load:runtime-config`
37
+
38
+ > [!IMPORTANT]
39
+ > Ensure you have deployed your infrastructure first before executing this command.
40
+
41
+ ## Useful links
42
+
43
+ - [Cloudscape website reference docs](TODO)
44
+ - [Learn more about NX](https://nx.dev/getting-started/intro)