@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.
Files changed (180) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/_README.md +57 -52
  3. package/mise.toml +2 -2
  4. package/mocks/browser.ts +1 -1
  5. package/mocks/fixtures/user.ts +14 -14
  6. package/mocks/handlers/userHandlers.ts +3 -3
  7. package/mocks/server.ts +1 -1
  8. package/package.json +30 -30
  9. package/playwright-report/html/index.html +90 -0
  10. package/playwright-report/sonar-results.xml +20 -0
  11. package/playwright.config.ts +3 -3
  12. package/scripts/removeDemo.cjs +105 -66
  13. package/src/README.md +81 -0
  14. package/src/app/App.tsx +18 -0
  15. package/src/{bootstrap.tsx → app/AppHTMLElement.tsx} +5 -5
  16. package/src/app/README.md +69 -0
  17. package/src/app/assets/css/index.ts +2 -0
  18. package/src/{assets → app/assets}/css/reset.css +3 -3
  19. package/src/app/config/queryClientConfig.ts +3 -0
  20. package/src/app/config/serviceContainer.ts +6 -0
  21. package/src/app/providers/Providers/Providers.tsx +18 -0
  22. package/src/app/providers/QueryClientProvider/QueryClientProvider.tsx +16 -0
  23. package/src/app/providers/ServiceProvider/ServiceContext.ts +8 -0
  24. package/src/app/providers/ServiceProvider/ServiceProvider.tsx +14 -0
  25. package/src/app/routing/Router.tsx +14 -0
  26. package/src/app/routing/appRoutes.ts +3 -0
  27. package/src/app/routing/routes.tsx +9 -0
  28. package/src/demo/README.md +149 -0
  29. package/src/demo/application/dtos/ArchitecturePrinciple.ts +5 -0
  30. package/src/{types/profileTypes.ts → demo/application/dtos/ProfileFormValues.ts} +1 -1
  31. package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCase.ts +11 -0
  32. package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCaseInterface.ts +5 -0
  33. package/src/demo/application/use-cases/GetDemoUsersUseCase.ts +11 -0
  34. package/src/demo/application/use-cases/GetDemoUsersUseCaseInterface.ts +5 -0
  35. package/src/demo/domain/entities/DemoUser.ts +7 -0
  36. package/src/demo/domain/repositories/ArchitecturePrincipleRepositoryInterface.d.ts +5 -0
  37. package/src/demo/domain/repositories/DemoUserRepositoryInterface.d.ts +5 -0
  38. package/src/demo/infrastructure/repositories/HttpDemoUserRepository.ts +29 -0
  39. package/src/demo/infrastructure/repositories/StaticArchitecturePrincipleRepository.ts +33 -0
  40. package/src/demo/serviceDemo.ts +40 -0
  41. package/src/demo/serviceIdentifiers.ts +6 -0
  42. package/src/demo/ui/components/DemoFooter/DemoFooter.tsx +17 -0
  43. package/src/demo/ui/components/DemoFooter/index.ts +1 -0
  44. package/src/demo/ui/components/DemoHeader/DemoHeader.tsx +25 -0
  45. package/src/demo/ui/components/DemoHeader/index.ts +1 -0
  46. package/src/{ui/molecules/Navigation/Navigation.tsx → demo/ui/components/DemoNavigation/DemoNavigation.tsx} +6 -6
  47. package/src/demo/ui/components/DemoNavigation/index.ts +1 -0
  48. package/src/{ui/atoms → demo/ui/components}/Input/Input.tsx +3 -3
  49. package/src/demo/ui/components/Input/index.ts +1 -0
  50. package/src/{ui/atoms → demo/ui/components}/Logo/Logo.tsx +2 -2
  51. package/src/demo/ui/components/Logo/index.ts +1 -0
  52. package/src/{ui/atoms → demo/ui/components}/NavLink/NavLink.tsx +3 -3
  53. package/src/demo/ui/components/NavLink/index.ts +1 -0
  54. package/src/{components → demo/ui/components}/TextInput/TextInput.tsx +3 -3
  55. package/src/demo/ui/components/TextInput/index.ts +1 -0
  56. package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.tsx +13 -13
  57. package/src/demo/ui/forms/ProfileForm/index.ts +1 -0
  58. package/src/demo/ui/hooks/useDemoUsersQuery/useDemoUsersQuery.ts +13 -0
  59. package/src/demo/ui/layouts/DemoLayout/DemoLayout.tsx +12 -0
  60. package/src/demo/ui/layouts/MainTemplate/MainTemplate.module.css +21 -0
  61. package/src/demo/ui/layouts/MainTemplate/MainTemplate.tsx +26 -0
  62. package/src/demo/ui/layouts/MainTemplate/index.ts +1 -0
  63. package/src/{pages/Demo/Demo.module.scss → demo/ui/pages/DemoHome/DemoHome.module.scss} +5 -5
  64. package/src/demo/ui/pages/DemoHome/DemoHome.tsx +117 -0
  65. package/src/demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.tsx +47 -0
  66. package/src/{pages/Demo → demo/ui/pages}/ReactHookFormDemo/ReactHookFormDemo.tsx +8 -8
  67. package/src/demo/ui/pages/ReactQueryDemo/ReactQueryDemo.module.scss +38 -0
  68. package/src/{pages/Demo → demo/ui/pages}/ReactQueryDemo/ReactQueryDemo.tsx +6 -5
  69. package/src/demo/ui/routing/demoAppRoutes.ts +6 -0
  70. package/src/demo/ui/routing/demoRoutes.tsx +31 -0
  71. package/src/main.ts +5 -5
  72. package/src/react-app-env.d.ts +1 -1
  73. package/src/shared/README.md +53 -0
  74. package/src/shared/application/README.md +49 -0
  75. package/src/shared/domain/README.md +52 -0
  76. package/src/shared/infrastructure/README.md +51 -0
  77. package/src/shared/infrastructure/http/constant.ts +16 -0
  78. package/src/{api → shared/infrastructure/http}/fetchApi.ts +3 -3
  79. package/src/shared/ui/README.md +50 -0
  80. package/src/shared/ui/hooks/useService/useService.ts +13 -0
  81. package/test-results/.last-run.json +4 -0
  82. package/tests/e2e/demo.test.ts +17 -0
  83. package/tests/e2e/fixture/playwright.fixture.ts +15 -0
  84. package/tests/{utils → e2e/utils}/createNetworkFixture.ts +2 -2
  85. package/tests/{utils → e2e/utils}/createTestWithCoverageFixture.ts +8 -8
  86. package/tests/{utils → e2e/utils}/playwright.globalSetup.ts +4 -4
  87. package/tests/{utils → e2e/utils}/playwright.globalTeardown.ts +3 -3
  88. package/{src/__tests__/bootstraps.test.tsx → tests/unit/app/AppHTMLElement.test.tsx} +14 -14
  89. package/tests/unit/demo/ui/components/DemoFooter/Footer.test.tsx +15 -0
  90. package/{src/components/Footer/__tests__ → tests/unit/demo/ui/components/DemoFooter}/__snapshots__/Footer.test.tsx.snap +3 -3
  91. package/tests/unit/demo/ui/components/DemoHeader/Header.test.tsx +15 -0
  92. package/tests/unit/demo/ui/components/DemoHeader/__snapshots__/Header.test.tsx.snap +64 -0
  93. package/tests/unit/demo/ui/components/Logo/Logo.test.tsx +12 -0
  94. package/tsconfig.json +4 -2
  95. package/vitest.config.mts +6 -2
  96. package/postcss.config.mjs +0 -5
  97. package/src/api/README.md +0 -65
  98. package/src/api/constant.ts +0 -16
  99. package/src/api/demoApi.ts +0 -10
  100. package/src/assets/README.md +0 -54
  101. package/src/assets/css/index.ts +0 -2
  102. package/src/components/App/App.tsx +0 -18
  103. package/src/components/App/index.ts +0 -1
  104. package/src/components/Footer/Footer.tsx +0 -16
  105. package/src/components/Footer/__tests__/Footer.test.tsx +0 -15
  106. package/src/components/Footer/index.ts +0 -1
  107. package/src/components/Header/Header.tsx +0 -17
  108. package/src/components/Header/__tests__/Header.test.tsx +0 -15
  109. package/src/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +0 -37
  110. package/src/components/Header/index.ts +0 -1
  111. package/src/components/README.md +0 -33
  112. package/src/components/TextInput/index.ts +0 -1
  113. package/src/config/README.md +0 -63
  114. package/src/config/index.ts +0 -1
  115. package/src/config/queryClientConfig.ts +0 -3
  116. package/src/forms/ProfileForm/index.ts +0 -1
  117. package/src/forms/README.md +0 -142
  118. package/src/hooks/README.md +0 -83
  119. package/src/hooks/api/queryKey.ts +0 -1
  120. package/src/hooks/api/useDemoApi.ts +0 -16
  121. package/src/pages/Demo/Demo.tsx +0 -236
  122. package/src/pages/Demo/ReactHookFormDemo/index.ts +0 -1
  123. package/src/pages/Demo/ReactHookFormDemo/routes.tsx +0 -7
  124. package/src/pages/Demo/ReactQueryDemo/index.ts +0 -1
  125. package/src/pages/Demo/ReactQueryDemo/routes.tsx +0 -7
  126. package/src/pages/Demo/__tests__/Demo.test.tsx +0 -15
  127. package/src/pages/Demo/__tests__/__snapshots__/Demo.test.tsx.snap +0 -557
  128. package/src/pages/Demo/index.ts +0 -1
  129. package/src/pages/Demo/routes.tsx +0 -8
  130. package/src/pages/Layout/Layout.tsx +0 -12
  131. package/src/pages/Layout/index.ts +0 -1
  132. package/src/pages/README.md +0 -91
  133. package/src/pages/UnexpectedError/UnexpectedError.module.css +0 -8
  134. package/src/pages/UnexpectedError/UnexpectedError.tsx +0 -22
  135. package/src/pages/UnexpectedError/index.ts +0 -1
  136. package/src/providers/QueryClientProvider/QueryClientProvider.tsx +0 -16
  137. package/src/providers/QueryClientProvider/index.ts +0 -1
  138. package/src/providers/README.md +0 -112
  139. package/src/providers/index.ts +0 -1
  140. package/src/providers/withProviders/index.ts +0 -1
  141. package/src/providers/withProviders/withProviders.tsx +0 -18
  142. package/src/routing/README.md +0 -76
  143. package/src/routing/appRoutes.ts +0 -5
  144. package/src/routing/routerFactory.ts +0 -12
  145. package/src/routing/routes.tsx +0 -13
  146. package/src/types/README.md +0 -62
  147. package/src/types/demoApi.ts +0 -25
  148. package/src/ui/README.md +0 -144
  149. package/src/ui/atoms/Input/index.ts +0 -1
  150. package/src/ui/atoms/Logo/__tests__/Logo.test.tsx +0 -12
  151. package/src/ui/atoms/Logo/index.ts +0 -1
  152. package/src/ui/atoms/NavLink/index.ts +0 -1
  153. package/src/ui/molecules/Navigation/index.ts +0 -1
  154. package/src/ui/organisms/Footer/Footer.module.css +0 -10
  155. package/src/ui/organisms/Footer/Footer.tsx +0 -10
  156. package/src/ui/organisms/Footer/index.ts +0 -1
  157. package/src/ui/organisms/Header/Header.module.css +0 -17
  158. package/src/ui/organisms/Header/Header.tsx +0 -16
  159. package/src/ui/organisms/Header/index.ts +0 -1
  160. package/src/ui/organisms/MainContent/MainContent.module.css +0 -3
  161. package/src/ui/organisms/MainContent/MainContent.tsx +0 -6
  162. package/src/ui/organisms/MainContent/index.ts +0 -1
  163. package/src/ui/templates/MainTemplate/MainTemplate.module.css +0 -5
  164. package/src/ui/templates/MainTemplate/MainTemplate.tsx +0 -26
  165. package/src/ui/templates/MainTemplate/index.ts +0 -1
  166. package/src/utils/README.md +0 -70
  167. package/tests/demo.test.ts +0 -17
  168. package/tests/fixture/playwright.fixture.ts +0 -15
  169. /package/src/{assets → app/assets}/css/mainBody.css +0 -0
  170. /package/src/{assets → demo/ui/assets}/images/logo.png +0 -0
  171. /package/src/{assets → demo/ui/assets}/images/templatePlugins.png +0 -0
  172. /package/src/{components/Footer/Footer.module.css → demo/ui/components/DemoFooter/DemoFooter.module.css} +0 -0
  173. /package/src/{components/Header/Header.module.css → demo/ui/components/DemoHeader/DemoHeader.module.css} +0 -0
  174. /package/src/{ui/molecules/Navigation/Navigation.module.css → demo/ui/components/DemoNavigation/DemoNavigation.module.css} +0 -0
  175. /package/src/{ui/atoms → demo/ui/components}/Input/Input.module.scss +0 -0
  176. /package/src/{ui/atoms → demo/ui/components}/Logo/Logo.module.css +0 -0
  177. /package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.module.scss +0 -0
  178. /package/src/{pages/Demo/ReactQueryDemo/ReactQueryDemo.module.scss → demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.module.scss} +0 -0
  179. /package/src/{pages/Demo/ReactHookFormDemo/ReactHookFormDemo.module.css → demo/ui/pages/ReactHookFormDemo/ReactHookFormDemo.module.scss} +0 -0
  180. /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>
@@ -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',
@@ -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('/api/demoApi.ts'));
8
-
9
- rmSync(resolveSrc('/assets/css/mainBody.css'));
10
- rmSync(resolveSrc('/assets/css/reset.css'));
11
- writeFileSync(resolveSrc('/assets/css/global.css'), ':root {\n --white-color: #fff;\n}\n', { encoding: 'utf-8' });
12
- let assetsIndex = readFileSync(resolveSrc('/assets/css/index.ts'), { encoding: 'utf-8' });
13
- assetsIndex = assetsIndex.replaceAll("import './reset.css';\nimport './mainBody.css';\n", "import './global.css';\n");
14
- writeFileSync(resolveSrc('/assets/css/index.ts'), assetsIndex, { encoding: 'utf-8' });
15
-
16
- rmSync(resolveSrc('/assets/images/logo.png'));
17
- rmSync(resolveSrc('/assets/images/templatePlugins.png'));
18
-
19
- rmSync(resolveSrc('/components/Footer'), { recursive: true });
20
- rmSync(resolveSrc('/components/Header'), { recursive: true });
21
- rmSync(resolveSrc('/components/TextInput'), { recursive: true });
22
-
23
- rmSync(resolveSrc('/forms/ProfileForm'), { recursive: true });
24
-
25
- rmSync(resolveSrc('/hooks/api/useDemoApi.ts'));
26
- let queryKey = readFileSync(resolveSrc('/hooks/api/queryKey.ts'), { encoding: 'utf-8' });
27
- queryKey = queryKey.replaceAll("export const demoQuery = () => ['dataWithDelay'];\n", '');
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('/pages/Home/routes.tsx'),
37
- "import type { RouteObject } from 'react-router';\n" +
38
- "import { Home } from './Home';\n" +
39
- '\n' +
40
- 'export const homeRoutes: RouteObject = {\n' +
41
- ' index: true,\n' +
42
- ' element: <Home />,\n' +
43
- '};\n',
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
- let appRoutes = readFileSync(resolveSrc('/routing/appRoutes.ts'), { encoding: 'utf-8' });
51
- appRoutes = appRoutes.replaceAll(
52
- "\n reactQueryDemo: '/reactQueryDemo',\n reactHookFormDemo: '/reactHookFormDemo',",
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
- writeFileSync(resolveSrc('/routing/appRoutes.ts'), appRoutes, { encoding: 'utf-8' });
56
- let routes = readFileSync(resolveSrc('/routing/routes.tsx'), { encoding: 'utf-8' });
57
- routes = routes.replaceAll(
58
- "import { demoRoutes } from '@Front/pages/Demo';",
59
- "import { homeRoutes } from '@Front/pages/Home';",
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
- routes = routes.replaceAll("\nimport { Layout } from '@Front/pages/Layout';", '');
62
- routes = routes.replaceAll("\nimport { UnexpectedError } from 'src/pages/UnexpectedError';", '');
63
- routes = routes.replaceAll(
64
- 'element: <Layout />,\n children: [demoRoutes],\n errorElement: <UnexpectedError />,',
65
- 'children: [homeRoutes],',
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
- writeFileSync(resolveSrc('/routing/routes.tsx'), routes, { encoding: 'utf-8' });
68
-
69
- rmSync(resolveSrc('/types/demoApi.ts'));
70
- rmSync(resolveSrc('/types/profileTypes.ts'));
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.
@@ -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 '@Front/components/App';
2
- import { StrictMode } from 'react';
3
- import { createRoot, type Root } from 'react-dom/client';
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 Bootstrap extends HTMLElement {
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('basename') ?? ''} />
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`
@@ -0,0 +1,2 @@
1
+ import "./reset.css";
2
+ import "./mainBody.css";
@@ -83,8 +83,8 @@ video {
83
83
  vertical-align: baseline;
84
84
  font: inherit;
85
85
  font-family:
86
- -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans',
87
- 'Helvetica Neue', sans-serif;
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, 'Courier New', monospace;
131
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
132
132
  }
@@ -0,0 +1,3 @@
1
+ import { QueryClient } from "@tanstack/react-query";
2
+
3
+ export const queryClient = new QueryClient();
@@ -0,0 +1,6 @@
1
+ import { serviceDemo } from "@Demo/serviceDemo";
2
+ import { Container } from "inversify";
3
+
4
+ export const serviceContainer = new Container();
5
+
6
+ serviceContainer.load(serviceDemo);
@@ -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,8 @@
1
+ import type { Container } from "inversify";
2
+ import { createContext } from "react";
3
+
4
+ export type ServiceContextValue = {
5
+ container: Container;
6
+ };
7
+
8
+ export const ServiceContext = createContext<ServiceContextValue | null>(null);
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ export const appRoutes = Object.freeze({
2
+ home: "/",
3
+ });
@@ -0,0 +1,9 @@
1
+ import { demoRoutes } from "@Demo/ui/routing/demoRoutes";
2
+ import type { RouteObject } from "react-router";
3
+
4
+ export const routeObject: RouteObject[] = [
5
+ {
6
+ path: "/",
7
+ children: [demoRoutes],
8
+ },
9
+ ];