@pplancq/react-template 2.8.17 → 2.10.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.
- package/CHANGELOG.md +66 -0
- package/_README.md +57 -52
- package/mise.toml +2 -2
- package/mocks/browser.ts +1 -1
- package/mocks/fixtures/user.ts +14 -14
- package/mocks/handlers/userHandlers.ts +3 -3
- package/mocks/server.ts +1 -1
- package/package.json +30 -30
- package/playwright-report/html/index.html +90 -0
- package/playwright-report/sonar-results.xml +20 -0
- package/playwright.config.ts +3 -3
- package/scripts/removeDemo.cjs +105 -66
- package/src/README.md +81 -0
- package/src/app/App.tsx +18 -0
- package/src/{bootstrap.tsx → app/AppHTMLElement.tsx} +5 -5
- package/src/app/README.md +69 -0
- package/src/app/assets/css/index.ts +2 -0
- package/src/{assets → app/assets}/css/reset.css +3 -3
- package/src/app/config/queryClientConfig.ts +3 -0
- package/src/app/config/serviceContainer.ts +6 -0
- package/src/app/providers/Providers/Providers.tsx +18 -0
- package/src/app/providers/QueryClientProvider/QueryClientProvider.tsx +16 -0
- package/src/app/providers/ServiceProvider/ServiceContext.ts +8 -0
- package/src/app/providers/ServiceProvider/ServiceProvider.tsx +14 -0
- package/src/app/routing/Router.tsx +14 -0
- package/src/app/routing/appRoutes.ts +3 -0
- package/src/app/routing/routes.tsx +9 -0
- package/src/demo/README.md +149 -0
- package/src/demo/application/dtos/ArchitecturePrinciple.ts +5 -0
- package/src/{types/profileTypes.ts → demo/application/dtos/ProfileFormValues.ts} +1 -1
- package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCase.ts +11 -0
- package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCaseInterface.ts +5 -0
- package/src/demo/application/use-cases/GetDemoUsersUseCase.ts +11 -0
- package/src/demo/application/use-cases/GetDemoUsersUseCaseInterface.ts +5 -0
- package/src/demo/domain/entities/DemoUser.ts +7 -0
- package/src/demo/domain/repositories/ArchitecturePrincipleRepositoryInterface.d.ts +5 -0
- package/src/demo/domain/repositories/DemoUserRepositoryInterface.d.ts +5 -0
- package/src/demo/infrastructure/repositories/HttpDemoUserRepository.ts +29 -0
- package/src/demo/infrastructure/repositories/StaticArchitecturePrincipleRepository.ts +33 -0
- package/src/demo/serviceDemo.ts +40 -0
- package/src/demo/serviceIdentifiers.ts +6 -0
- package/src/demo/ui/components/DemoFooter/DemoFooter.tsx +17 -0
- package/src/demo/ui/components/DemoFooter/index.ts +1 -0
- package/src/demo/ui/components/DemoHeader/DemoHeader.tsx +25 -0
- package/src/demo/ui/components/DemoHeader/index.ts +1 -0
- package/src/{ui/molecules/Navigation/Navigation.tsx → demo/ui/components/DemoNavigation/DemoNavigation.tsx} +6 -6
- package/src/demo/ui/components/DemoNavigation/index.ts +1 -0
- package/src/{ui/atoms → demo/ui/components}/Input/Input.tsx +3 -3
- package/src/demo/ui/components/Input/index.ts +1 -0
- package/src/{ui/atoms → demo/ui/components}/Logo/Logo.tsx +2 -2
- package/src/demo/ui/components/Logo/index.ts +1 -0
- package/src/{ui/atoms → demo/ui/components}/NavLink/NavLink.tsx +3 -3
- package/src/demo/ui/components/NavLink/index.ts +1 -0
- package/src/{components → demo/ui/components}/TextInput/TextInput.tsx +3 -3
- package/src/demo/ui/components/TextInput/index.ts +1 -0
- package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.tsx +13 -13
- package/src/demo/ui/forms/ProfileForm/index.ts +1 -0
- package/src/demo/ui/hooks/useDemoUsersQuery/useDemoUsersQuery.ts +13 -0
- package/src/demo/ui/layouts/DemoLayout/DemoLayout.tsx +12 -0
- package/src/demo/ui/layouts/MainTemplate/MainTemplate.module.css +21 -0
- package/src/demo/ui/layouts/MainTemplate/MainTemplate.tsx +26 -0
- package/src/demo/ui/layouts/MainTemplate/index.ts +1 -0
- package/src/{pages/Demo/Demo.module.scss → demo/ui/pages/DemoHome/DemoHome.module.scss} +5 -5
- package/src/demo/ui/pages/DemoHome/DemoHome.tsx +117 -0
- package/src/demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.tsx +47 -0
- package/src/{pages/Demo → demo/ui/pages}/ReactHookFormDemo/ReactHookFormDemo.tsx +8 -8
- package/src/demo/ui/pages/ReactQueryDemo/ReactQueryDemo.module.scss +38 -0
- package/src/{pages/Demo → demo/ui/pages}/ReactQueryDemo/ReactQueryDemo.tsx +6 -5
- package/src/demo/ui/routing/demoAppRoutes.ts +6 -0
- package/src/demo/ui/routing/demoRoutes.tsx +31 -0
- package/src/main.ts +5 -5
- package/src/react-app-env.d.ts +1 -1
- package/src/shared/README.md +53 -0
- package/src/shared/application/README.md +49 -0
- package/src/shared/domain/README.md +52 -0
- package/src/shared/infrastructure/README.md +51 -0
- package/src/shared/infrastructure/http/constant.ts +16 -0
- package/src/{api → shared/infrastructure/http}/fetchApi.ts +3 -3
- package/src/shared/ui/README.md +50 -0
- package/src/shared/ui/hooks/useService/useService.ts +13 -0
- package/test-results/.last-run.json +4 -0
- package/tests/e2e/demo.test.ts +17 -0
- package/tests/e2e/fixture/playwright.fixture.ts +15 -0
- package/tests/{utils → e2e/utils}/createNetworkFixture.ts +2 -2
- package/tests/{utils → e2e/utils}/createTestWithCoverageFixture.ts +8 -8
- package/tests/{utils → e2e/utils}/playwright.globalSetup.ts +4 -4
- package/tests/{utils → e2e/utils}/playwright.globalTeardown.ts +3 -3
- package/{src/__tests__/bootstraps.test.tsx → tests/unit/app/AppHTMLElement.test.tsx} +14 -14
- package/tests/unit/demo/ui/components/DemoFooter/Footer.test.tsx +15 -0
- package/{src/components/Footer/__tests__ → tests/unit/demo/ui/components/DemoFooter}/__snapshots__/Footer.test.tsx.snap +3 -3
- package/tests/unit/demo/ui/components/DemoHeader/Header.test.tsx +15 -0
- package/tests/unit/demo/ui/components/DemoHeader/__snapshots__/Header.test.tsx.snap +64 -0
- package/tests/unit/demo/ui/components/Logo/Logo.test.tsx +12 -0
- package/tsconfig.json +4 -2
- package/vitest.config.mts +6 -2
- package/postcss.config.mjs +0 -5
- package/src/api/README.md +0 -65
- package/src/api/constant.ts +0 -16
- package/src/api/demoApi.ts +0 -10
- package/src/assets/README.md +0 -54
- package/src/assets/css/index.ts +0 -2
- package/src/components/App/App.tsx +0 -18
- package/src/components/App/index.ts +0 -1
- package/src/components/Footer/Footer.tsx +0 -16
- package/src/components/Footer/__tests__/Footer.test.tsx +0 -15
- package/src/components/Footer/index.ts +0 -1
- package/src/components/Header/Header.tsx +0 -17
- package/src/components/Header/__tests__/Header.test.tsx +0 -15
- package/src/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +0 -37
- package/src/components/Header/index.ts +0 -1
- package/src/components/README.md +0 -33
- package/src/components/TextInput/index.ts +0 -1
- package/src/config/README.md +0 -63
- package/src/config/index.ts +0 -1
- package/src/config/queryClientConfig.ts +0 -3
- package/src/forms/ProfileForm/index.ts +0 -1
- package/src/forms/README.md +0 -142
- package/src/hooks/README.md +0 -83
- package/src/hooks/api/queryKey.ts +0 -1
- package/src/hooks/api/useDemoApi.ts +0 -16
- package/src/pages/Demo/Demo.tsx +0 -236
- package/src/pages/Demo/ReactHookFormDemo/index.ts +0 -1
- package/src/pages/Demo/ReactHookFormDemo/routes.tsx +0 -7
- package/src/pages/Demo/ReactQueryDemo/index.ts +0 -1
- package/src/pages/Demo/ReactQueryDemo/routes.tsx +0 -7
- package/src/pages/Demo/__tests__/Demo.test.tsx +0 -15
- package/src/pages/Demo/__tests__/__snapshots__/Demo.test.tsx.snap +0 -557
- package/src/pages/Demo/index.ts +0 -1
- package/src/pages/Demo/routes.tsx +0 -8
- package/src/pages/Layout/Layout.tsx +0 -12
- package/src/pages/Layout/index.ts +0 -1
- package/src/pages/README.md +0 -91
- package/src/pages/UnexpectedError/UnexpectedError.module.css +0 -8
- package/src/pages/UnexpectedError/UnexpectedError.tsx +0 -22
- package/src/pages/UnexpectedError/index.ts +0 -1
- package/src/providers/QueryClientProvider/QueryClientProvider.tsx +0 -16
- package/src/providers/QueryClientProvider/index.ts +0 -1
- package/src/providers/README.md +0 -112
- package/src/providers/index.ts +0 -1
- package/src/providers/withProviders/index.ts +0 -1
- package/src/providers/withProviders/withProviders.tsx +0 -18
- package/src/routing/README.md +0 -76
- package/src/routing/appRoutes.ts +0 -5
- package/src/routing/routerFactory.ts +0 -12
- package/src/routing/routes.tsx +0 -13
- package/src/types/README.md +0 -62
- package/src/types/demoApi.ts +0 -25
- package/src/ui/README.md +0 -144
- package/src/ui/atoms/Input/index.ts +0 -1
- package/src/ui/atoms/Logo/__tests__/Logo.test.tsx +0 -12
- package/src/ui/atoms/Logo/index.ts +0 -1
- package/src/ui/atoms/NavLink/index.ts +0 -1
- package/src/ui/molecules/Navigation/index.ts +0 -1
- package/src/ui/organisms/Footer/Footer.module.css +0 -10
- package/src/ui/organisms/Footer/Footer.tsx +0 -10
- package/src/ui/organisms/Footer/index.ts +0 -1
- package/src/ui/organisms/Header/Header.module.css +0 -17
- package/src/ui/organisms/Header/Header.tsx +0 -16
- package/src/ui/organisms/Header/index.ts +0 -1
- package/src/ui/organisms/MainContent/MainContent.module.css +0 -3
- package/src/ui/organisms/MainContent/MainContent.tsx +0 -6
- package/src/ui/organisms/MainContent/index.ts +0 -1
- package/src/ui/templates/MainTemplate/MainTemplate.module.css +0 -5
- package/src/ui/templates/MainTemplate/MainTemplate.tsx +0 -26
- package/src/ui/templates/MainTemplate/index.ts +0 -1
- package/src/utils/README.md +0 -70
- package/tests/demo.test.ts +0 -17
- package/tests/fixture/playwright.fixture.ts +0 -15
- /package/src/{assets → app/assets}/css/mainBody.css +0 -0
- /package/src/{assets → demo/ui/assets}/images/logo.png +0 -0
- /package/src/{assets → demo/ui/assets}/images/templatePlugins.png +0 -0
- /package/src/{components/Footer/Footer.module.css → demo/ui/components/DemoFooter/DemoFooter.module.css} +0 -0
- /package/src/{components/Header/Header.module.css → demo/ui/components/DemoHeader/DemoHeader.module.css} +0 -0
- /package/src/{ui/molecules/Navigation/Navigation.module.css → demo/ui/components/DemoNavigation/DemoNavigation.module.css} +0 -0
- /package/src/{ui/atoms → demo/ui/components}/Input/Input.module.scss +0 -0
- /package/src/{ui/atoms → demo/ui/components}/Logo/Logo.module.css +0 -0
- /package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.module.scss +0 -0
- /package/src/{pages/Demo/ReactQueryDemo/ReactQueryDemo.module.scss → demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.module.scss} +0 -0
- /package/src/{pages/Demo/ReactHookFormDemo/ReactHookFormDemo.module.css → demo/ui/pages/ReactHookFormDemo/ReactHookFormDemo.module.scss} +0 -0
- /package/src/{types/api.ts → shared/infrastructure/http/fetchApi.type.d.ts} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<testExecutions version="1">
|
|
2
|
+
<file path="/Users/paul/dev/@pplancq/dev-tools/packages/react-template/tests/e2e/demo.test.ts">
|
|
3
|
+
<testCase name="Demo Test › should have a title" duration="218">
|
|
4
|
+
</testCase>
|
|
5
|
+
<testCase name="Demo Test › should data fetching" duration="225">
|
|
6
|
+
</testCase>
|
|
7
|
+
</file>
|
|
8
|
+
<file path="/Users/paul/dev/@pplancq/dev-tools/packages/react-template/tests/e2e/demo.test.ts">
|
|
9
|
+
<testCase name="Demo Test › should have a title" duration="210">
|
|
10
|
+
</testCase>
|
|
11
|
+
<testCase name="Demo Test › should data fetching" duration="226">
|
|
12
|
+
</testCase>
|
|
13
|
+
</file>
|
|
14
|
+
<file path="/Users/paul/dev/@pplancq/dev-tools/packages/react-template/tests/e2e/demo.test.ts">
|
|
15
|
+
<testCase name="Demo Test › should have a title" duration="518">
|
|
16
|
+
</testCase>
|
|
17
|
+
<testCase name="Demo Test › should data fetching" duration="509">
|
|
18
|
+
</testCase>
|
|
19
|
+
</file>
|
|
20
|
+
</testExecutions>
|
package/playwright.config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { defineConfig, devices } from '@playwright/test';
|
|
|
3
3
|
const appUrl = 'http://localhost:3000';
|
|
4
4
|
|
|
5
5
|
export default defineConfig({
|
|
6
|
-
testDir: 'tests',
|
|
6
|
+
testDir: 'tests/e2e',
|
|
7
7
|
outputDir: 'test-results',
|
|
8
8
|
fullyParallel: true,
|
|
9
9
|
forbidOnly: Boolean(process.env.CI),
|
|
@@ -18,8 +18,8 @@ export default defineConfig({
|
|
|
18
18
|
{ outputFile: 'playwright-report/sonar-results.xml', sonarcloud: true },
|
|
19
19
|
],
|
|
20
20
|
],
|
|
21
|
-
globalSetup: 'tests/utils/playwright.globalSetup.ts',
|
|
22
|
-
globalTeardown: 'tests/utils/playwright.globalTeardown.ts',
|
|
21
|
+
globalSetup: 'tests/e2e/utils/playwright.globalSetup.ts',
|
|
22
|
+
globalTeardown: 'tests/e2e/utils/playwright.globalTeardown.ts',
|
|
23
23
|
use: {
|
|
24
24
|
baseURL: appUrl,
|
|
25
25
|
trace: 'retain-on-failure',
|
package/scripts/removeDemo.cjs
CHANGED
|
@@ -4,79 +4,118 @@ const { resolve } = require('node:path');
|
|
|
4
4
|
|
|
5
5
|
const resolveSrc = path => resolve(__dirname, `../src${path}`);
|
|
6
6
|
|
|
7
|
-
rmSync(resolveSrc('/
|
|
8
|
-
|
|
9
|
-
rmSync(resolveSrc('/assets/css/mainBody.css'));
|
|
10
|
-
|
|
11
|
-
writeFileSync(resolveSrc('/assets/css/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
writeFileSync(resolveSrc('/hooks/api/queryKey.ts'), queryKey, { encoding: 'utf-8' });
|
|
29
|
-
|
|
30
|
-
rmSync(resolveSrc('/pages/Demo'), { recursive: true });
|
|
31
|
-
rmSync(resolveSrc('/pages/Layout'), { recursive: true });
|
|
32
|
-
rmSync(resolveSrc('/pages/UnexpectedError'), { recursive: true });
|
|
33
|
-
mkdirSync(resolveSrc('/pages/Home'));
|
|
34
|
-
writeFileSync(resolveSrc('/pages/Home/index.ts'), "export { homeRoutes } from './routes';\n", { encoding: 'utf-8' });
|
|
7
|
+
rmSync(resolveSrc('/demo'), { recursive: true, force: true });
|
|
8
|
+
|
|
9
|
+
rmSync(resolveSrc('/app/assets/css/mainBody.css'), { force: true });
|
|
10
|
+
writeFileSync(resolveSrc('/app/assets/css/global.css'), ':root {\n --white-color: #fff;\n}\n', { encoding: 'utf-8' });
|
|
11
|
+
writeFileSync(resolveSrc('/app/assets/css/index.ts'), "import './reset.css';\nimport './global.css';\n", {
|
|
12
|
+
encoding: 'utf-8',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
mkdirSync(resolveSrc('/home/domain'), { recursive: true });
|
|
16
|
+
writeFileSync(resolveSrc('/home/domain/.gitkeep'), '', { encoding: 'utf-8' });
|
|
17
|
+
|
|
18
|
+
mkdirSync(resolveSrc('/home/application'), { recursive: true });
|
|
19
|
+
writeFileSync(resolveSrc('/home/application/.gitkeep'), '', { encoding: 'utf-8' });
|
|
20
|
+
|
|
21
|
+
mkdirSync(resolveSrc('/home/infrastructure'), { recursive: true });
|
|
22
|
+
writeFileSync(resolveSrc('/home/infrastructure/.gitkeep'), '', { encoding: 'utf-8' });
|
|
23
|
+
|
|
24
|
+
writeFileSync(resolveSrc('/home/serviceIdentifiers.ts'), 'export const HOME_SERVICES = Object.freeze({});\n', {
|
|
25
|
+
encoding: 'utf-8',
|
|
26
|
+
});
|
|
27
|
+
|
|
35
28
|
writeFileSync(
|
|
36
|
-
resolveSrc('/
|
|
37
|
-
|
|
38
|
-
"import {
|
|
39
|
-
'
|
|
40
|
-
'export const
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
29
|
+
resolveSrc('/home/serviceHome.ts'),
|
|
30
|
+
[
|
|
31
|
+
"import { ContainerModule } from 'inversify';",
|
|
32
|
+
'',
|
|
33
|
+
'export const serviceHome: ContainerModule = new ContainerModule(_options => {',
|
|
34
|
+
' // Register your home context services here',
|
|
35
|
+
'});',
|
|
36
|
+
'',
|
|
37
|
+
].join('\n'),
|
|
44
38
|
{ encoding: 'utf-8' },
|
|
45
39
|
);
|
|
46
|
-
writeFileSync(resolveSrc('/pages/Home/Home.tsx'), 'export const Home = () => {\n return <div>HomePage</div>;\n};\n', {
|
|
47
|
-
encoding: 'utf-8',
|
|
48
|
-
});
|
|
49
40
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
mkdirSync(resolveSrc('/home/ui/pages/Home'), { recursive: true });
|
|
42
|
+
writeFileSync(
|
|
43
|
+
resolveSrc('/home/ui/pages/Home/Home.tsx'),
|
|
44
|
+
[
|
|
45
|
+
'export const Home = () => {',
|
|
46
|
+
' return (',
|
|
47
|
+
' <section>',
|
|
48
|
+
' <h1>Home</h1>',
|
|
49
|
+
' <p>Your demo context has been removed. Start building your first bounded context from here.</p>',
|
|
50
|
+
' </section>',
|
|
51
|
+
' );',
|
|
52
|
+
'};',
|
|
53
|
+
'',
|
|
54
|
+
].join('\n'),
|
|
55
|
+
{ encoding: 'utf-8' },
|
|
54
56
|
);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
|
|
58
|
+
mkdirSync(resolveSrc('/home/ui/routing'), { recursive: true });
|
|
59
|
+
writeFileSync(
|
|
60
|
+
resolveSrc('/home/ui/routing/homeAppRoutes.ts'),
|
|
61
|
+
['export const homeAppRoutes = Object.freeze({', " home: '/',", '});', ''].join('\n'),
|
|
62
|
+
{ encoding: 'utf-8' },
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
writeFileSync(
|
|
66
|
+
resolveSrc('/home/ui/routing/homeRoutes.tsx'),
|
|
67
|
+
[
|
|
68
|
+
"import { Home } from '@Home/ui/pages/Home/Home';",
|
|
69
|
+
"import type { RouteObject } from 'react-router';",
|
|
70
|
+
'',
|
|
71
|
+
'export const homeRoutes: RouteObject = {',
|
|
72
|
+
' children: [',
|
|
73
|
+
' {',
|
|
74
|
+
' index: true,',
|
|
75
|
+
' element: <Home />,',
|
|
76
|
+
' },',
|
|
77
|
+
' ],',
|
|
78
|
+
'};',
|
|
79
|
+
'',
|
|
80
|
+
].join('\n'),
|
|
81
|
+
{ encoding: 'utf-8' },
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
writeFileSync(
|
|
85
|
+
resolveSrc('/app/config/serviceContainer.ts'),
|
|
86
|
+
[
|
|
87
|
+
"import { serviceHome } from '@Home/serviceHome';",
|
|
88
|
+
"import { Container } from 'inversify';",
|
|
89
|
+
'',
|
|
90
|
+
'export const serviceContainer = new Container();',
|
|
91
|
+
'',
|
|
92
|
+
'serviceContainer.load(serviceHome);',
|
|
93
|
+
'',
|
|
94
|
+
].join('\n'),
|
|
95
|
+
{ encoding: 'utf-8' },
|
|
60
96
|
);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
routes
|
|
64
|
-
|
|
65
|
-
|
|
97
|
+
|
|
98
|
+
writeFileSync(
|
|
99
|
+
resolveSrc('/app/routing/routes.tsx'),
|
|
100
|
+
[
|
|
101
|
+
"import { homeRoutes } from '@Home/ui/routing/homeRoutes';",
|
|
102
|
+
"import type { RouteObject } from 'react-router';",
|
|
103
|
+
'',
|
|
104
|
+
'export const routeObject: RouteObject[] = [',
|
|
105
|
+
' {',
|
|
106
|
+
" path: '/',",
|
|
107
|
+
' children: [homeRoutes],',
|
|
108
|
+
' },',
|
|
109
|
+
'];',
|
|
110
|
+
'',
|
|
111
|
+
].join('\n'),
|
|
112
|
+
{ encoding: 'utf-8' },
|
|
66
113
|
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
rmSync(resolveSrc('/ui/atoms/Input'), { recursive: true });
|
|
73
|
-
rmSync(resolveSrc('/ui/atoms/Logo'), { recursive: true });
|
|
74
|
-
rmSync(resolveSrc('/ui/atoms/NavLink'), { recursive: true });
|
|
75
|
-
rmSync(resolveSrc('/ui/molecules/Navigation'), { recursive: true });
|
|
76
|
-
rmSync(resolveSrc('/ui/organisms/Footer'), { recursive: true });
|
|
77
|
-
rmSync(resolveSrc('/ui/organisms/Header'), { recursive: true });
|
|
78
|
-
rmSync(resolveSrc('/ui/organisms/MainContent'), { recursive: true });
|
|
79
|
-
rmSync(resolveSrc('/ui/templates/MainTemplate'), { recursive: true });
|
|
114
|
+
|
|
115
|
+
const tsconfigJson = JSON.parse(readFileSync(resolve(__dirname, '../tsconfig.json'), { encoding: 'utf-8' }));
|
|
116
|
+
delete tsconfigJson.compilerOptions.paths['@Demo/*'];
|
|
117
|
+
tsconfigJson.compilerOptions.paths['@Home/*'] = ['./src/home/*'];
|
|
118
|
+
writeFileSync(resolve(__dirname, '../tsconfig.json'), JSON.stringify(tsconfigJson, null, 2), { encoding: 'utf-8' });
|
|
80
119
|
|
|
81
120
|
const packagesJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), { encoding: 'utf-8' }));
|
|
82
121
|
const { 'remove:demo': _, ...scripts } = packagesJson.scripts;
|
package/src/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `src`
|
|
2
|
+
|
|
3
|
+
This directory contains the template source code and its explanatory documentation.
|
|
4
|
+
|
|
5
|
+
## Architecture Philosophy
|
|
6
|
+
|
|
7
|
+
This starter follows a **hybrid architecture** combining two complementary patterns:
|
|
8
|
+
|
|
9
|
+
- **Clean Architecture** (Robert C. Martin) — code is organized into horizontal layers that enforce a strict
|
|
10
|
+
dependency direction. Dependencies always flow **inward toward the domain**. The domain is the most protected
|
|
11
|
+
layer; it has zero external dependencies.
|
|
12
|
+
- **DDD Bounded Contexts** — code is also organized into vertical slices, each representing an isolated business
|
|
13
|
+
domain. A bounded context owns its full stack: domain, application, infrastructure, and UI.
|
|
14
|
+
|
|
15
|
+
The result is a structure where every folder belongs to either a **horizontal layer** (domain, application,
|
|
16
|
+
infrastructure, ui) or a **vertical slice** (a bounded context such as `demo`), and both dimensions are always
|
|
17
|
+
visible.
|
|
18
|
+
|
|
19
|
+
## Top-level structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── app/ # Application bootstrap layer (not a bounded context)
|
|
24
|
+
├── shared/ # Shared kernel — genuinely cross-context code
|
|
25
|
+
│ ├── domain/
|
|
26
|
+
│ ├── application/
|
|
27
|
+
│ ├── infrastructure/
|
|
28
|
+
│ └── ui/
|
|
29
|
+
└── demo/ # Example bounded context (domain / application / infrastructure / ui)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Fundamental dependency rule
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
UI → Application → Domain
|
|
36
|
+
Infrastructure -.→ Domain (implements its interfaces)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **Domain** depends on nothing. Pure TypeScript.
|
|
40
|
+
- **Application** depends on Domain only. Orchestrates use cases, defines infrastructure interfaces.
|
|
41
|
+
- **Infrastructure** implements interfaces defined by Domain/Application. May use external libraries.
|
|
42
|
+
- **UI** depends on Application (use cases) and Domain (DTOs). No business logic.
|
|
43
|
+
|
|
44
|
+
This rule applies **within each bounded context** and **within the shared kernel**.
|
|
45
|
+
|
|
46
|
+
## Path aliases
|
|
47
|
+
|
|
48
|
+
Each first-class section of the source tree has a TypeScript path alias. Use these aliases instead of long
|
|
49
|
+
relative paths; they signal architectural intent and prevent accidental cross-context coupling.
|
|
50
|
+
|
|
51
|
+
| Alias | Maps to |
|
|
52
|
+
| ----------- | -------------- |
|
|
53
|
+
| `@App/*` | `src/app/*` |
|
|
54
|
+
| `@Shared/*` | `src/shared/*` |
|
|
55
|
+
| `@Demo/*` | `src/demo/*` |
|
|
56
|
+
| `@Mocks/*` | `mocks/*` |
|
|
57
|
+
|
|
58
|
+
When you add a real bounded context alongside `demo`, add its own alias to `tsconfig.json`.
|
|
59
|
+
|
|
60
|
+
## Table of contents
|
|
61
|
+
|
|
62
|
+
1. [`app/README.md`](./app/README.md) — application bootstrap, composition root, routing, and providers
|
|
63
|
+
2. [`shared/README.md`](./shared/README.md) — shared kernel concept and usage rules
|
|
64
|
+
3. [`shared/domain/README.md`](./shared/domain/README.md) — shared pure contracts, types, and domain rules
|
|
65
|
+
4. [`shared/application/README.md`](./shared/application/README.md) — shared application-layer abstractions
|
|
66
|
+
5. [`shared/infrastructure/README.md`](./shared/infrastructure/README.md) — shared technical adapters and helpers
|
|
67
|
+
6. [`shared/ui/README.md`](./shared/ui/README.md) — shared UI-facing hooks and utilities
|
|
68
|
+
7. [`demo/README.md`](./demo/README.md) — the example bounded context and how to replicate it
|
|
69
|
+
|
|
70
|
+
## Suggested reading order
|
|
71
|
+
|
|
72
|
+
If you are discovering the template for the first time:
|
|
73
|
+
|
|
74
|
+
1. This file — understand the big picture
|
|
75
|
+
2. `app` — see how the application boots and wires everything together
|
|
76
|
+
3. `shared/domain` — understand why the domain layer is the most protected
|
|
77
|
+
4. `shared` — understand what the shared kernel is and what it must not become
|
|
78
|
+
5. `demo` — walk through a complete bounded context layer by layer
|
|
79
|
+
|
|
80
|
+
That sequence starts with the overall architecture, moves to the heart of Clean Architecture (domain purity),
|
|
81
|
+
then shows how a real bounded context puts all the pieces together.
|
package/src/app/App.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { queryClient } from "@App/config/queryClientConfig";
|
|
2
|
+
import { serviceContainer } from "@App/config/serviceContainer";
|
|
3
|
+
import { Providers } from "@App/providers/Providers/Providers";
|
|
4
|
+
import { Router } from "@App/routing/Router";
|
|
5
|
+
|
|
6
|
+
import "@App/assets/css";
|
|
7
|
+
|
|
8
|
+
type AppProps = {
|
|
9
|
+
basename?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const App = ({ basename }: AppProps) => {
|
|
13
|
+
return (
|
|
14
|
+
<Providers queryClient={queryClient} container={serviceContainer}>
|
|
15
|
+
<Router basename={basename} />
|
|
16
|
+
</Providers>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { App } from
|
|
2
|
-
import { StrictMode } from
|
|
3
|
-
import { createRoot, type Root } from
|
|
1
|
+
import { App } from "@App/App";
|
|
2
|
+
import { StrictMode } from "react";
|
|
3
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
4
4
|
|
|
5
5
|
// eslint-disable-next-line import/no-default-export
|
|
6
|
-
export default class
|
|
6
|
+
export default class AppHTMLElement extends HTMLElement {
|
|
7
7
|
private readonly root: Root;
|
|
8
8
|
|
|
9
9
|
private isMounted = false;
|
|
@@ -18,7 +18,7 @@ export default class Bootstrap extends HTMLElement {
|
|
|
18
18
|
this.isMounted = true;
|
|
19
19
|
this.root.render(
|
|
20
20
|
<StrictMode>
|
|
21
|
-
<App basename={this.getAttribute(
|
|
21
|
+
<App basename={this.getAttribute("basename") ?? ""} />
|
|
22
22
|
</StrictMode>,
|
|
23
23
|
);
|
|
24
24
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# `app`
|
|
2
|
+
|
|
3
|
+
The `app` folder is the **application bootstrap layer**. It is not a bounded context — it has no business
|
|
4
|
+
domain of its own. Its single responsibility is to assemble all the pieces (providers, router, DI container,
|
|
5
|
+
contexts) and expose the application to the browser.
|
|
6
|
+
|
|
7
|
+
Path alias: `@App/*`
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
| Subfolder / file | Role |
|
|
12
|
+
| -------------------- | -------------------------------------------------------------------------------- |
|
|
13
|
+
| `App.tsx` | Root React component — composes providers and router |
|
|
14
|
+
| `AppHTMLElement.tsx` | Custom-element bootstrap (`<app-react>`). Mounts/unmounts the React root |
|
|
15
|
+
| `assets/` | Global CSS reset, base styles, and app-wide static assets |
|
|
16
|
+
| `config/` | App-wide configuration and the **composition root** (`serviceContainer.ts`) |
|
|
17
|
+
| `providers/` | React provider components: `ServiceProvider`, `QueryClientProvider`, `Providers` |
|
|
18
|
+
| `routing/` | Top-level router and route aggregation |
|
|
19
|
+
|
|
20
|
+
## Composition root
|
|
21
|
+
|
|
22
|
+
`config/serviceContainer.ts` is the **composition root** of the dependency injection system. It is the single
|
|
23
|
+
place in the application where the Inversify container is created and all bounded-context service collections
|
|
24
|
+
are loaded:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { serviceDemo } from "@Demo/serviceDemo";
|
|
28
|
+
import { Container } from "inversify";
|
|
29
|
+
|
|
30
|
+
export const serviceContainer = new Container();
|
|
31
|
+
serviceContainer.load(serviceDemo);
|
|
32
|
+
// add more context modules here as the application grows
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The composition root is the **only** place that knows about concrete implementations. Every other layer receives
|
|
36
|
+
dependencies through interfaces and the DI container.
|
|
37
|
+
|
|
38
|
+
## Provider tree
|
|
39
|
+
|
|
40
|
+
The `Providers` component wraps the application in its provider tree. The order matters:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ServiceProvider ← makes the DI container available via React context
|
|
44
|
+
└── QueryClientProvider ← makes React Query available
|
|
45
|
+
└── {children} ← the rest of the application
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`ServiceProvider` must be outermost so that any inner provider can use `useService` to resolve DI services.
|
|
49
|
+
|
|
50
|
+
## Custom element bootstrap
|
|
51
|
+
|
|
52
|
+
`AppHTMLElement.tsx` registers the application as a
|
|
53
|
+
[custom element](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define) (`<app-react>`).
|
|
54
|
+
This allows the React app to be embedded in any HTML host without tight coupling to a specific framework or
|
|
55
|
+
page structure. The custom element mounts a React root in `connectedCallback` and unmounts it in
|
|
56
|
+
`disconnectedCallback`.
|
|
57
|
+
|
|
58
|
+
## Dependency rules for `app`
|
|
59
|
+
|
|
60
|
+
- ✅ Can import from `@Shared/*` and any bounded context (e.g., `@Demo/*`)
|
|
61
|
+
- ✅ Responsible for high-level orchestration only
|
|
62
|
+
- ❌ Must not contain business rules or feature-specific logic
|
|
63
|
+
- ❌ Generic helpers belong in `shared`, not here
|
|
64
|
+
|
|
65
|
+
## Avoid
|
|
66
|
+
|
|
67
|
+
- Business rules or domain objects
|
|
68
|
+
- Feature-specific orchestration that belongs to a bounded context
|
|
69
|
+
- Generic helpers that should live in `shared`
|
|
@@ -83,8 +83,8 @@ video {
|
|
|
83
83
|
vertical-align: baseline;
|
|
84
84
|
font: inherit;
|
|
85
85
|
font-family:
|
|
86
|
-
-apple-system, BlinkMacSystemFont,
|
|
87
|
-
|
|
86
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans",
|
|
87
|
+
"Helvetica Neue", sans-serif;
|
|
88
88
|
font-size: 100%;
|
|
89
89
|
-webkit-font-smoothing: antialiased;
|
|
90
90
|
-moz-osx-font-smoothing: grayscale;
|
|
@@ -128,5 +128,5 @@ table {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
code {
|
|
131
|
-
font-family: source-code-pro, Menlo, Monaco, Consolas,
|
|
131
|
+
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
132
132
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { QueryClientProvider } from "@App/providers/QueryClientProvider/QueryClientProvider";
|
|
2
|
+
import { ServiceProvider } from "@App/providers/ServiceProvider/ServiceProvider";
|
|
3
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
4
|
+
import type { Container } from "inversify";
|
|
5
|
+
import type { PropsWithChildren } from "react";
|
|
6
|
+
|
|
7
|
+
type ProvidersProps = PropsWithChildren<{
|
|
8
|
+
queryClient: QueryClient;
|
|
9
|
+
container: Container;
|
|
10
|
+
}>;
|
|
11
|
+
|
|
12
|
+
export const Providers = ({ queryClient, container, children }: ProvidersProps) => {
|
|
13
|
+
return (
|
|
14
|
+
<ServiceProvider container={container}>
|
|
15
|
+
<QueryClientProvider queryClient={queryClient}>{children}</QueryClientProvider>
|
|
16
|
+
</ServiceProvider>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type QueryClient, QueryClientProvider as ReactQueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
|
|
5
|
+
type ClientProviderProps = {
|
|
6
|
+
queryClient: QueryClient;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const QueryClientProvider = ({ children, queryClient }: PropsWithChildren<ClientProviderProps>) => {
|
|
10
|
+
return (
|
|
11
|
+
<ReactQueryClientProvider client={queryClient}>
|
|
12
|
+
{children}
|
|
13
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
14
|
+
</ReactQueryClientProvider>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ServiceContext } from "@App/providers/ServiceProvider/ServiceContext";
|
|
2
|
+
import type { Container } from "inversify";
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
import { useRef } from "react";
|
|
5
|
+
|
|
6
|
+
export type ServiceProviderProps = PropsWithChildren<{
|
|
7
|
+
container: Container;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export const ServiceProvider = ({ container, children }: ServiceProviderProps) => {
|
|
11
|
+
const containerRef = useRef({ container });
|
|
12
|
+
|
|
13
|
+
return <ServiceContext.Provider value={containerRef.current}>{children}</ServiceContext.Provider>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createBrowserRouter, RouterProvider } from "react-router";
|
|
2
|
+
import { routeObject } from "./routes";
|
|
3
|
+
|
|
4
|
+
type RouterProps = {
|
|
5
|
+
basename?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Router = ({ basename }: RouterProps = {}) => {
|
|
9
|
+
const router = createBrowserRouter(routeObject, {
|
|
10
|
+
basename,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return <RouterProvider router={router} />;
|
|
14
|
+
};
|