@aws/nx-plugin 0.62.0 → 0.62.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-THIRD-PARTY +30 -3
- package/package.json +3 -4
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/mcp-server/schema.d.ts +7 -2
- package/src/mcp-server/schema.js +2 -2
- package/src/mcp-server/schema.js.map +1 -1
- package/src/mcp-server/tools/create-workspace-command.js +8 -2
- package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
- package/src/mcp-server/tools/general-guidance.js +4 -1
- package/src/mcp-server/tools/general-guidance.js.map +1 -1
- package/src/mcp-server/tools/generator-guide.js +7 -4
- package/src/mcp-server/tools/generator-guide.js.map +1 -1
- package/src/mcp-server/tools/list-generators.js +4 -1
- package/src/mcp-server/tools/list-generators.js.map +1 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/mcp-server/files/resources/sample-guidance.ts.template +1 -1
- package/src/ts/mcp-server/files/tools/add.ts.template +6 -4
- package/src/ts/mcp-server/generator.js +1 -5
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +35 -20
- package/src/ts/nx-plugin/files/mcp-server/schema.ts.template +1 -1
- package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +7 -5
- package/src/ts/nx-plugin/files/mcp-server/tools/general-guidance.ts.template +5 -3
- package/src/ts/nx-plugin/files/mcp-server/tools/generator-guide.ts.template +9 -7
- package/src/ts/nx-plugin/files/mcp-server/tools/list-generators.ts.template +6 -4
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +883 -85
- package/src/ts/react-website/app/files/app/{src → cloudscape/src}/app.tsx.template +1 -1
- package/src/ts/react-website/app/files/app/cloudscape/src/components/alert.tsx.template +3 -0
- package/src/ts/react-website/app/files/app/cloudscape/src/components/spinner.tsx.template +3 -0
- package/src/ts/react-website/app/files/app/{src → common/src}/config.ts.template +1 -1
- package/src/ts/react-website/app/files/app/none/src/app.tsx.template +8 -0
- package/src/ts/react-website/app/files/app/none/src/components/AppLayout/index.tsx.template +124 -0
- package/src/ts/react-website/app/files/app/none/src/components/alert.tsx.template +14 -0
- package/src/ts/react-website/app/files/app/none/src/components/spinner.tsx.template +7 -0
- package/src/ts/react-website/app/files/app/none/src/main.tsx.template +31 -0
- package/src/ts/react-website/app/files/app/none/src/styles.css.template +201 -0
- package/src/ts/react-website/app/files/tanstack-router/common/src/routeTree.gen.ts.template +59 -0
- package/src/ts/react-website/app/files/tanstack-router/none/src/routes/index.tsx.template +14 -0
- package/src/ts/react-website/app/generator.d.ts +2 -0
- package/src/ts/react-website/app/generator.js +30 -16
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/app/schema.d.ts +2 -1
- package/src/ts/react-website/app/schema.json +22 -0
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +348 -185
- package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +15 -15
- package/src/ts/react-website/cognito-auth/generator.js +14 -40
- package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/utils.d.ts +7 -0
- package/src/ts/react-website/cognito-auth/utils.js +92 -0
- package/src/ts/react-website/cognito-auth/utils.js.map +1 -0
- package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +1 -1
- package/src/utils/versions.d.ts +9 -10
- package/src/utils/versions.js +9 -12
- package/src/utils/versions.js.map +1 -1
- package/src/ts/react-website/app/files/tanstack-router/src/routeTree.gen.ts.template +0 -111
- /package/src/ts/react-website/app/files/app/{src → cloudscape/src}/components/AppLayout/index.tsx.template +0 -0
- /package/src/ts/react-website/app/files/app/{src → cloudscape/src}/hooks/useAppLayout.tsx.template +0 -0
- /package/src/ts/react-website/app/files/app/{src → cloudscape/src}/main.tsx.template +0 -0
- /package/src/ts/react-website/app/files/app/{src → cloudscape/src}/styles.css.template +0 -0
- /package/src/ts/react-website/app/files/app/{README.md.template → common/README.md.template} +0 -0
- /package/src/ts/react-website/app/files/tanstack-router/{src → cloudscape/src}/routes/index.tsx.template +0 -0
- /package/src/ts/react-website/app/files/tanstack-router/{src → common/src}/routes/__root.tsx.template +0 -0
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import { SupportedStyles } from '@nx/react';
|
|
6
5
|
import { IacProviderOption } from '../../../utils/iac';
|
|
6
|
+
import { UxProviderOption } from '../../../utils/ux';
|
|
7
7
|
|
|
8
8
|
export interface TsReactWebsiteGeneratorSchema {
|
|
9
9
|
name: string;
|
|
@@ -11,5 +11,6 @@ export interface TsReactWebsiteGeneratorSchema {
|
|
|
11
11
|
skipInstall?: boolean;
|
|
12
12
|
enableTanstackRouter?: boolean;
|
|
13
13
|
enableTailwind?: boolean;
|
|
14
|
+
uxProvider?: UxProviderOption;
|
|
14
15
|
iacProvider: IacProviderOption;
|
|
15
16
|
}
|
|
@@ -30,6 +30,28 @@
|
|
|
30
30
|
"default": "packages",
|
|
31
31
|
"x-prompt": "What directory would you like to store your application in?"
|
|
32
32
|
},
|
|
33
|
+
"uxProvider": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "The preferred UX provider.",
|
|
36
|
+
"enum": ["None", "Cloudscape"],
|
|
37
|
+
"x-priority": "important",
|
|
38
|
+
"default": "Cloudscape",
|
|
39
|
+
"x-prompt": {
|
|
40
|
+
"message": "Which UX provider should we scaffold for this app?",
|
|
41
|
+
"type": "list",
|
|
42
|
+
"items": [
|
|
43
|
+
{
|
|
44
|
+
"value": "None",
|
|
45
|
+
"label": "None (plain React/Vite without a UI kit)"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"value": "Cloudscape",
|
|
49
|
+
"label": "Cloudscape (AWS-first components with ready-made layouts)"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"default": "None"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
33
55
|
"enableTailwind": {
|
|
34
56
|
"description": "Enable TailwindCSS for utility-first styling.",
|
|
35
57
|
"type": "boolean",
|
|
@@ -1,9 +1,344 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`cognito-auth generator (uxProvider=Cloudscape) > should update AppLayout > app-layout-with-auth 1`] = `
|
|
4
|
+
"import { useAuth } from 'react-oidc-context';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { createContext, useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { NavItems } from './navitems';
|
|
8
|
+
import Config from '../../config';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
BreadcrumbGroup,
|
|
12
|
+
BreadcrumbGroupProps,
|
|
13
|
+
SideNavigation,
|
|
14
|
+
TopNavigation,
|
|
15
|
+
} from '@cloudscape-design/components';
|
|
16
|
+
|
|
17
|
+
import CloudscapeAppLayout, {
|
|
18
|
+
AppLayoutProps,
|
|
19
|
+
} from '@cloudscape-design/components/app-layout';
|
|
20
|
+
|
|
21
|
+
import { matchByPath, useLocation, useNavigate } from '@tanstack/react-router';
|
|
22
|
+
import { Outlet } from '@tanstack/react-router';
|
|
23
|
+
|
|
24
|
+
const getBreadcrumbs = (
|
|
25
|
+
pathName: string,
|
|
26
|
+
search: string,
|
|
27
|
+
defaultBreadcrumb: string,
|
|
28
|
+
availableRoutes?: string[],
|
|
29
|
+
) => {
|
|
30
|
+
const segments = [
|
|
31
|
+
defaultBreadcrumb,
|
|
32
|
+
...pathName.split('/').filter((segment) => segment !== ''),
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
return segments.map((segment, i) => {
|
|
36
|
+
const href =
|
|
37
|
+
i === 0
|
|
38
|
+
? '/'
|
|
39
|
+
: \`/\${segments
|
|
40
|
+
.slice(1, i + 1)
|
|
41
|
+
.join('/')
|
|
42
|
+
.replace('//', '/')}\`;
|
|
43
|
+
|
|
44
|
+
const matched =
|
|
45
|
+
!availableRoutes || availableRoutes.find((r) => matchByPath(r, href, {}));
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
href: matched ? \`\${href}\${search}\` : '#',
|
|
49
|
+
text: segment,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export interface AppLayoutContext {
|
|
55
|
+
appLayoutProps: AppLayoutProps;
|
|
56
|
+
setAppLayoutProps: (props: AppLayoutProps) => void;
|
|
57
|
+
displayHelpPanel: (helpContent: React.ReactNode) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Context for updating/retrieving the AppLayout.
|
|
62
|
+
*/
|
|
63
|
+
export const AppLayoutContext = createContext({
|
|
64
|
+
appLayoutProps: {},
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
66
|
+
setAppLayoutProps: (_: AppLayoutProps) => {},
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
68
|
+
displayHelpPanel: (_: React.ReactNode) => {},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Defines the App layout and contains logic for routing.
|
|
73
|
+
*/
|
|
74
|
+
const AppLayout: React.FC = () => {
|
|
75
|
+
const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
|
|
76
|
+
const navigate = useNavigate();
|
|
77
|
+
const appLayout = React.useRef<AppLayoutProps.Ref>(null);
|
|
78
|
+
const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
|
|
79
|
+
BreadcrumbGroupProps.Item[]
|
|
80
|
+
>([{ text: '/', href: '/' }]);
|
|
81
|
+
const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
|
|
82
|
+
const { pathname, search } = useLocation();
|
|
83
|
+
const setAppLayoutPropsSafe = useCallback(
|
|
84
|
+
(props: AppLayoutProps) => {
|
|
85
|
+
JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
|
|
86
|
+
setAppLayoutProps(props);
|
|
87
|
+
},
|
|
88
|
+
[appLayoutProps],
|
|
89
|
+
);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const breadcrumbs = getBreadcrumbs(
|
|
92
|
+
pathname,
|
|
93
|
+
Object.entries(search).reduce((p, [k, v]) => p + \`\${k}=\${v}\`, ''),
|
|
94
|
+
'/',
|
|
95
|
+
);
|
|
96
|
+
setActiveBreadcrumbs(breadcrumbs);
|
|
97
|
+
}, [pathname, search]);
|
|
98
|
+
const onNavigate = useCallback(
|
|
99
|
+
(
|
|
100
|
+
e: CustomEvent<{
|
|
101
|
+
href: string;
|
|
102
|
+
external?: boolean;
|
|
103
|
+
}>,
|
|
104
|
+
) => {
|
|
105
|
+
if (!e.detail.external) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
setAppLayoutPropsSafe({
|
|
108
|
+
contentType: undefined,
|
|
109
|
+
});
|
|
110
|
+
navigate({ to: e.detail.href });
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[navigate, setAppLayoutPropsSafe],
|
|
114
|
+
);
|
|
115
|
+
return (
|
|
116
|
+
<AppLayoutContext.Provider
|
|
117
|
+
value={{
|
|
118
|
+
appLayoutProps,
|
|
119
|
+
setAppLayoutProps: setAppLayoutPropsSafe,
|
|
120
|
+
displayHelpPanel: (helpContent: React.ReactNode) => {
|
|
121
|
+
setAppLayoutPropsSafe({ tools: helpContent, toolsHide: false });
|
|
122
|
+
appLayout.current?.openTools();
|
|
123
|
+
},
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<TopNavigation
|
|
127
|
+
identity={{
|
|
128
|
+
href: '/',
|
|
129
|
+
title: Config.applicationName,
|
|
130
|
+
logo: {
|
|
131
|
+
src: Config.logo,
|
|
132
|
+
},
|
|
133
|
+
}}
|
|
134
|
+
utilities={[
|
|
135
|
+
{
|
|
136
|
+
type: 'menu-dropdown',
|
|
137
|
+
text: \`\${user?.profile?.['cognito:username']}\`,
|
|
138
|
+
iconName: 'user-profile-active',
|
|
139
|
+
onItemClick: (e) => {
|
|
140
|
+
if (e.detail.id === 'signout') {
|
|
141
|
+
removeUser();
|
|
142
|
+
signoutRedirect({
|
|
143
|
+
post_logout_redirect_uri: window.location.origin,
|
|
144
|
+
extraQueryParams: {
|
|
145
|
+
redirect_uri: window.location.origin,
|
|
146
|
+
response_type: 'code',
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
clearStaleState();
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
items: [{ id: 'signout', text: 'Sign out' }],
|
|
153
|
+
},
|
|
154
|
+
]}
|
|
155
|
+
/>
|
|
156
|
+
<CloudscapeAppLayout
|
|
157
|
+
ref={appLayout}
|
|
158
|
+
breadcrumbs={
|
|
159
|
+
<BreadcrumbGroup onFollow={onNavigate} items={activeBreadcrumbs} />
|
|
160
|
+
}
|
|
161
|
+
toolsHide
|
|
162
|
+
navigation={
|
|
163
|
+
<SideNavigation
|
|
164
|
+
header={{ text: Config.applicationName, href: '/' }}
|
|
165
|
+
activeHref={pathname}
|
|
166
|
+
onFollow={onNavigate}
|
|
167
|
+
items={NavItems}
|
|
168
|
+
/>
|
|
169
|
+
}
|
|
170
|
+
content={<Outlet />}
|
|
171
|
+
{...appLayoutProps}
|
|
172
|
+
/>
|
|
173
|
+
</AppLayoutContext.Provider>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export default AppLayout;
|
|
178
|
+
"
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
exports[`cognito-auth generator (uxProvider=None) > should update AppLayout > app-layout-with-auth 1`] = `
|
|
182
|
+
"import { useAuth } from 'react-oidc-context';
|
|
183
|
+
import * as React from 'react';
|
|
184
|
+
|
|
185
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
186
|
+
|
|
187
|
+
import Config from '../../config';
|
|
188
|
+
import { Link, useLocation, useMatchRoute } from '@tanstack/react-router';
|
|
189
|
+
|
|
190
|
+
const getBreadcrumbs = (
|
|
191
|
+
matchRoute: ReturnType<typeof useMatchRoute>,
|
|
192
|
+
pathName: string,
|
|
193
|
+
search: string,
|
|
194
|
+
defaultBreadcrumb: string,
|
|
195
|
+
availableRoutes?: string[],
|
|
196
|
+
) => {
|
|
197
|
+
const segments = [
|
|
198
|
+
defaultBreadcrumb,
|
|
199
|
+
...pathName.split('/').filter((segment) => segment !== ''),
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
return segments.map((segment, i) => {
|
|
203
|
+
const href =
|
|
204
|
+
i === 0
|
|
205
|
+
? '/'
|
|
206
|
+
: \`/\${segments
|
|
207
|
+
.slice(1, i + 1)
|
|
208
|
+
.join('/')
|
|
209
|
+
.replace('//', '/')}\`;
|
|
210
|
+
|
|
211
|
+
const matched =
|
|
212
|
+
!availableRoutes || availableRoutes.find((r) => matchRoute({ to: href }));
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
href: matched ? \`\${href}\${search}\` : '#',
|
|
216
|
+
text: segment,
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Defines the App layout and contains logic for routing.
|
|
223
|
+
*/
|
|
224
|
+
const AppLayout: React.FC<React.PropsWithChildren> = ({ children }) => {
|
|
225
|
+
const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
|
|
226
|
+
const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
|
|
227
|
+
const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
|
|
228
|
+
{
|
|
229
|
+
href: string;
|
|
230
|
+
text: string;
|
|
231
|
+
}[]
|
|
232
|
+
>([{ text: '/', href: '/' }]);
|
|
233
|
+
const matchRoute = useMatchRoute();
|
|
234
|
+
const { pathname, search } = useLocation();
|
|
235
|
+
const navItems = useMemo(() => [{ to: '/', label: 'Home' }], []);
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
const breadcrumbs = getBreadcrumbs(
|
|
238
|
+
matchRoute,
|
|
239
|
+
pathname,
|
|
240
|
+
Object.entries(search).reduce((p, [k, v]) => p + \`\${k}=\${v}\`, ''),
|
|
241
|
+
'Home',
|
|
242
|
+
);
|
|
243
|
+
setActiveBreadcrumbs(breadcrumbs);
|
|
244
|
+
}, [matchRoute, pathname, search]);
|
|
245
|
+
return (
|
|
246
|
+
<div className="app-shell">
|
|
247
|
+
<header className="app-header">
|
|
248
|
+
<div className="app-header-inner">
|
|
249
|
+
<div className="brand">
|
|
250
|
+
<a href="/">
|
|
251
|
+
<img
|
|
252
|
+
src={Config.logo}
|
|
253
|
+
alt={\`\${Config.applicationName} logo\`}
|
|
254
|
+
className="brand-logo"
|
|
255
|
+
/>
|
|
256
|
+
<span className="brand-name">{Config.applicationName}</span>
|
|
257
|
+
</a>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<nav className="app-nav">
|
|
261
|
+
{navItems.map((item) => (
|
|
262
|
+
<Link
|
|
263
|
+
key={item.to}
|
|
264
|
+
to={item.to}
|
|
265
|
+
className={pathname === item.to ? 'active' : undefined}
|
|
266
|
+
>
|
|
267
|
+
{item.label}
|
|
268
|
+
</Link>
|
|
269
|
+
))}
|
|
270
|
+
</nav>
|
|
271
|
+
<div className="user-greeting">
|
|
272
|
+
<span>Hi, {\`\${user?.profile?.['cognito:username']}\`}</span>
|
|
273
|
+
<button
|
|
274
|
+
type="button"
|
|
275
|
+
className="signout-link"
|
|
276
|
+
onClick={() => {
|
|
277
|
+
removeUser();
|
|
278
|
+
signoutRedirect({
|
|
279
|
+
post_logout_redirect_uri: window.location.origin,
|
|
280
|
+
extraQueryParams: {
|
|
281
|
+
redirect_uri: window.location.origin,
|
|
282
|
+
response_type: 'code',
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
clearStaleState();
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
Sign out
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
<div className="user-greeting">
|
|
292
|
+
<span>Hi, {\`\${user?.profile?.['cognito:username']}\`}</span>
|
|
293
|
+
<button
|
|
294
|
+
type="button"
|
|
295
|
+
className="signout-link"
|
|
296
|
+
onClick={() => {
|
|
297
|
+
removeUser();
|
|
298
|
+
signoutRedirect({
|
|
299
|
+
post_logout_redirect_uri: window.location.origin,
|
|
300
|
+
extraQueryParams: {
|
|
301
|
+
redirect_uri: window.location.origin,
|
|
302
|
+
response_type: 'code',
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
clearStaleState();
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
308
|
+
Sign out
|
|
309
|
+
</button>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</header>
|
|
313
|
+
<main className="app-main">
|
|
314
|
+
<nav className="breadcrumbs" aria-label="Breadcrumb">
|
|
315
|
+
{activeBreadcrumbs.map((crumb, index) => (
|
|
316
|
+
<span className="breadcrumb-segment" key={crumb.href || index}>
|
|
317
|
+
{index > 0 && <span className="breadcrumb-separator">/</span>}
|
|
318
|
+
{index === activeBreadcrumbs.length - 1 ? (
|
|
319
|
+
<span className="breadcrumb-current">{crumb.text}</span>
|
|
320
|
+
) : (
|
|
321
|
+
<Link to={crumb.href}>{crumb.text}</Link>
|
|
322
|
+
)}
|
|
323
|
+
</span>
|
|
324
|
+
))}
|
|
325
|
+
</nav>
|
|
326
|
+
|
|
327
|
+
<section className="card">{children}</section>
|
|
328
|
+
</main>
|
|
329
|
+
</div>
|
|
330
|
+
);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export default AppLayout;
|
|
334
|
+
"
|
|
335
|
+
`;
|
|
336
|
+
|
|
3
337
|
exports[`cognito-auth generator > should generate files > cognito-auth-component 1`] = `
|
|
4
338
|
"import React, { PropsWithChildren, useEffect } from 'react';
|
|
5
339
|
import { AuthProvider, AuthProviderProps, useAuth } from 'react-oidc-context';
|
|
6
|
-
import { Alert
|
|
340
|
+
import { Alert } from '../alert';
|
|
341
|
+
import { Spinner } from '../spinner';
|
|
7
342
|
import { useRuntimeConfig } from '../../hooks/useRuntimeConfig';
|
|
8
343
|
|
|
9
344
|
/**
|
|
@@ -22,7 +357,9 @@ const CognitoAuth: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
22
357
|
}
|
|
23
358
|
return (
|
|
24
359
|
<Alert type="error" header="Runtime config configuration error">
|
|
25
|
-
|
|
360
|
+
<p>
|
|
361
|
+
The cognitoProps have not been configured in the runtime-config.json.
|
|
362
|
+
</p>
|
|
26
363
|
</Alert>
|
|
27
364
|
);
|
|
28
365
|
}
|
|
@@ -53,16 +390,20 @@ const CognitoAuthInternal: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
53
390
|
|
|
54
391
|
if (auth.isAuthenticated) {
|
|
55
392
|
return children;
|
|
56
|
-
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (auth.error) {
|
|
57
396
|
return (
|
|
58
397
|
<Alert type="error" header="Configuration error">
|
|
59
|
-
|
|
60
|
-
|
|
398
|
+
<p>
|
|
399
|
+
Error contacting Cognito. Please check your runtime-config.json is
|
|
400
|
+
configured with the correct endpoints.
|
|
401
|
+
</p>
|
|
61
402
|
</Alert>
|
|
62
403
|
);
|
|
63
|
-
} else {
|
|
64
|
-
return <Spinner />;
|
|
65
404
|
}
|
|
405
|
+
|
|
406
|
+
return <Spinner />;
|
|
66
407
|
};
|
|
67
408
|
|
|
68
409
|
export default CognitoAuth;
|
|
@@ -264,184 +605,6 @@ export class UserIdentity extends Construct {
|
|
|
264
605
|
|
|
265
606
|
exports[`cognito-auth generator > should not be able to run the generator multiple times 1`] = `[Error: This generator has already been run on test-project.]`;
|
|
266
607
|
|
|
267
|
-
exports[`cognito-auth generator > should update AppLayout > app-layout-with-auth 1`] = `
|
|
268
|
-
"import { useAuth } from 'react-oidc-context';
|
|
269
|
-
import * as React from 'react';
|
|
270
|
-
import { createContext, useCallback, useEffect, useState } from 'react';
|
|
271
|
-
import { NavItems } from './navitems';
|
|
272
|
-
import Config from '../../config';
|
|
273
|
-
|
|
274
|
-
import {
|
|
275
|
-
BreadcrumbGroup,
|
|
276
|
-
BreadcrumbGroupProps,
|
|
277
|
-
SideNavigation,
|
|
278
|
-
TopNavigation,
|
|
279
|
-
} from '@cloudscape-design/components';
|
|
280
|
-
|
|
281
|
-
import CloudscapeAppLayout, {
|
|
282
|
-
AppLayoutProps,
|
|
283
|
-
} from '@cloudscape-design/components/app-layout';
|
|
284
|
-
|
|
285
|
-
import { matchByPath, useLocation, useNavigate } from '@tanstack/react-router';
|
|
286
|
-
import { Outlet } from '@tanstack/react-router';
|
|
287
|
-
|
|
288
|
-
const getBreadcrumbs = (
|
|
289
|
-
pathName: string,
|
|
290
|
-
search: string,
|
|
291
|
-
defaultBreadcrumb: string,
|
|
292
|
-
availableRoutes?: string[],
|
|
293
|
-
) => {
|
|
294
|
-
const segments = [
|
|
295
|
-
defaultBreadcrumb,
|
|
296
|
-
...pathName.split('/').filter((segment) => segment !== ''),
|
|
297
|
-
];
|
|
298
|
-
|
|
299
|
-
return segments.map((segment, i) => {
|
|
300
|
-
const href =
|
|
301
|
-
i === 0
|
|
302
|
-
? '/'
|
|
303
|
-
: \`/\${segments
|
|
304
|
-
.slice(1, i + 1)
|
|
305
|
-
.join('/')
|
|
306
|
-
.replace('//', '/')}\`;
|
|
307
|
-
|
|
308
|
-
const matched =
|
|
309
|
-
!availableRoutes || availableRoutes.find((r) => matchByPath(r, href, {}));
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
href: matched ? \`\${href}\${search}\` : '#',
|
|
313
|
-
text: segment,
|
|
314
|
-
};
|
|
315
|
-
});
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
export interface AppLayoutContext {
|
|
319
|
-
appLayoutProps: AppLayoutProps;
|
|
320
|
-
setAppLayoutProps: (props: AppLayoutProps) => void;
|
|
321
|
-
displayHelpPanel: (helpContent: React.ReactNode) => void;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Context for updating/retrieving the AppLayout.
|
|
326
|
-
*/
|
|
327
|
-
export const AppLayoutContext = createContext({
|
|
328
|
-
appLayoutProps: {},
|
|
329
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
330
|
-
setAppLayoutProps: (_: AppLayoutProps) => {},
|
|
331
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
332
|
-
displayHelpPanel: (_: React.ReactNode) => {},
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Defines the App layout and contains logic for routing.
|
|
337
|
-
*/
|
|
338
|
-
const AppLayout: React.FC = () => {
|
|
339
|
-
const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
|
|
340
|
-
const navigate = useNavigate();
|
|
341
|
-
const appLayout = React.useRef<AppLayoutProps.Ref>(null);
|
|
342
|
-
const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
|
|
343
|
-
BreadcrumbGroupProps.Item[]
|
|
344
|
-
>([{ text: '/', href: '/' }]);
|
|
345
|
-
const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
|
|
346
|
-
const { pathname, search } = useLocation();
|
|
347
|
-
const setAppLayoutPropsSafe = useCallback(
|
|
348
|
-
(props: AppLayoutProps) => {
|
|
349
|
-
JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
|
|
350
|
-
setAppLayoutProps(props);
|
|
351
|
-
},
|
|
352
|
-
[appLayoutProps],
|
|
353
|
-
);
|
|
354
|
-
useEffect(() => {
|
|
355
|
-
const breadcrumbs = getBreadcrumbs(
|
|
356
|
-
pathname,
|
|
357
|
-
Object.entries(search).reduce((p, [k, v]) => p + \`\${k}=\${v}\`, ''),
|
|
358
|
-
'/',
|
|
359
|
-
);
|
|
360
|
-
setActiveBreadcrumbs(breadcrumbs);
|
|
361
|
-
}, [pathname, search]);
|
|
362
|
-
const onNavigate = useCallback(
|
|
363
|
-
(
|
|
364
|
-
e: CustomEvent<{
|
|
365
|
-
href: string;
|
|
366
|
-
external?: boolean;
|
|
367
|
-
}>,
|
|
368
|
-
) => {
|
|
369
|
-
if (!e.detail.external) {
|
|
370
|
-
e.preventDefault();
|
|
371
|
-
setAppLayoutPropsSafe({
|
|
372
|
-
contentType: undefined,
|
|
373
|
-
});
|
|
374
|
-
navigate({ to: e.detail.href });
|
|
375
|
-
}
|
|
376
|
-
},
|
|
377
|
-
[navigate, setAppLayoutPropsSafe],
|
|
378
|
-
);
|
|
379
|
-
return (
|
|
380
|
-
<AppLayoutContext.Provider
|
|
381
|
-
value={{
|
|
382
|
-
appLayoutProps,
|
|
383
|
-
setAppLayoutProps: setAppLayoutPropsSafe,
|
|
384
|
-
displayHelpPanel: (helpContent: React.ReactNode) => {
|
|
385
|
-
setAppLayoutPropsSafe({ tools: helpContent, toolsHide: false });
|
|
386
|
-
appLayout.current?.openTools();
|
|
387
|
-
},
|
|
388
|
-
}}
|
|
389
|
-
>
|
|
390
|
-
<TopNavigation
|
|
391
|
-
identity={{
|
|
392
|
-
href: '/',
|
|
393
|
-
title: Config.applicationName,
|
|
394
|
-
logo: {
|
|
395
|
-
src: Config.logo,
|
|
396
|
-
},
|
|
397
|
-
}}
|
|
398
|
-
utilities={[
|
|
399
|
-
{
|
|
400
|
-
type: 'menu-dropdown',
|
|
401
|
-
text: \`\${user?.profile?.['cognito:username']}\`,
|
|
402
|
-
iconName: 'user-profile-active',
|
|
403
|
-
onItemClick: (e) => {
|
|
404
|
-
if (e.detail.id === 'signout') {
|
|
405
|
-
removeUser();
|
|
406
|
-
signoutRedirect({
|
|
407
|
-
post_logout_redirect_uri: window.location.origin,
|
|
408
|
-
extraQueryParams: {
|
|
409
|
-
redirect_uri: window.location.origin,
|
|
410
|
-
response_type: 'code',
|
|
411
|
-
},
|
|
412
|
-
});
|
|
413
|
-
clearStaleState();
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
items: [{ id: 'signout', text: 'Sign out' }],
|
|
417
|
-
},
|
|
418
|
-
]}
|
|
419
|
-
/>
|
|
420
|
-
<CloudscapeAppLayout
|
|
421
|
-
ref={appLayout}
|
|
422
|
-
breadcrumbs={
|
|
423
|
-
<BreadcrumbGroup onFollow={onNavigate} items={activeBreadcrumbs} />
|
|
424
|
-
}
|
|
425
|
-
toolsHide
|
|
426
|
-
navigation={
|
|
427
|
-
<SideNavigation
|
|
428
|
-
header={{ text: Config.applicationName, href: '/' }}
|
|
429
|
-
activeHref={pathname}
|
|
430
|
-
onFollow={onNavigate}
|
|
431
|
-
items={NavItems}
|
|
432
|
-
/>
|
|
433
|
-
}
|
|
434
|
-
content={<Outlet />}
|
|
435
|
-
{...appLayoutProps}
|
|
436
|
-
/>
|
|
437
|
-
</AppLayoutContext.Provider>
|
|
438
|
-
);
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
export default AppLayout;
|
|
442
|
-
"
|
|
443
|
-
`;
|
|
444
|
-
|
|
445
608
|
exports[`cognito-auth generator > should update main.tsx when RuntimeConfigProvider exists > main-tsx-with-runtime-config 1`] = `
|
|
446
609
|
"import CognitoAuth from './components/CognitoAuth';
|
|
447
610
|
import RuntimeConfigProvider from './components/RuntimeConfig';
|
package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useEffect } from 'react';
|
|
2
2
|
import { AuthProvider, AuthProviderProps, useAuth } from 'react-oidc-context';
|
|
3
|
-
import { Alert
|
|
3
|
+
import { Alert } from '../alert';
|
|
4
|
+
import { Spinner } from '../spinner';
|
|
4
5
|
import { useRuntimeConfig } from '../../hooks/useRuntimeConfig';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -15,16 +16,11 @@ const CognitoAuth: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
15
16
|
if (!cognitoProps) {
|
|
16
17
|
if (import.meta.env.MODE === 'serve-local') {
|
|
17
18
|
// In serve-local mode with no cognitoProps available, we skip login
|
|
18
|
-
return
|
|
19
|
-
<AuthProvider>{children}</AuthProvider>
|
|
20
|
-
);
|
|
19
|
+
return <AuthProvider>{children}</AuthProvider>;
|
|
21
20
|
}
|
|
22
21
|
return (
|
|
23
|
-
<Alert
|
|
24
|
-
|
|
25
|
-
header="Runtime config configuration error"
|
|
26
|
-
>
|
|
27
|
-
The cognitoProps have not been configured in the runtime-config.json.
|
|
22
|
+
<Alert type="error" header="Runtime config configuration error">
|
|
23
|
+
<p>The cognitoProps have not been configured in the runtime-config.json.</p>
|
|
28
24
|
</Alert>
|
|
29
25
|
);
|
|
30
26
|
}
|
|
@@ -51,20 +47,24 @@ const CognitoAuthInternal: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
51
47
|
if (!auth.isAuthenticated && !auth.isLoading) {
|
|
52
48
|
auth.signinRedirect();
|
|
53
49
|
}
|
|
54
|
-
}, [auth])
|
|
50
|
+
}, [auth]);
|
|
55
51
|
|
|
56
52
|
if (auth.isAuthenticated) {
|
|
57
53
|
return children;
|
|
58
|
-
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (auth.error) {
|
|
59
57
|
return (
|
|
60
58
|
<Alert type="error" header="Configuration error">
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
<p>
|
|
60
|
+
Error contacting Cognito. Please check your runtime-config.json is configured
|
|
61
|
+
with the correct endpoints.
|
|
62
|
+
</p>
|
|
63
63
|
</Alert>
|
|
64
64
|
);
|
|
65
|
-
} else {
|
|
66
|
-
return <Spinner />;
|
|
67
65
|
}
|
|
66
|
+
|
|
67
|
+
return <Spinner />;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
export default CognitoAuth;
|