Skip to content

Commit 594cbef

Browse files
Боровский ДмитрийБоровский Дмитрий
authored andcommitted
feat(create-arui-scripts-app): add simple routing in cli
1 parent 3c31a95 commit 594cbef

20 files changed

Lines changed: 413 additions & 29 deletions

packages/create-arui-scripts-app/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ npx create-arui-scripts-app my-app --yes
4545
- SSR-сервер на **Hapi** с рендерингом приложения (`renderToString`) и гидрацией на клиенте
4646
- транспилятор (**swc** / babel / tsc) и тест-раннер (**Jest** / Vitest)
4747
- опционально **e2e** (**Cypress** / Playwright) с конфигом и хелперами
48+
- опционально **React Router**
4849
- CSS-модули, полифилы (`core-js`), `experimentalReactCompiler`, docker registry, preset
4950
- опционально **arui-presets-lint** (eslint, prettier, stylelint, knip, secretlint, lefthook)
5051
- опциональная установка зависимостей сразу после генерации
@@ -61,8 +62,8 @@ npx create-arui-scripts-app my-app --yes --lint --install
6162
пример компонента со стилями и тестом, `global-definitions.d.ts`, `.gitignore`, `README.md`,
6263
`.yarnrc.yml``nodeLinker: node-modules`),
6364
а также в зависимости от ответов - серверная точка входа на Hapi, store на RTK, полифилы,
64-
`vitest.config.ts`, e2e-каркас (Playwright или Cypress) и конфиги `arui-presets-lint`
65-
(`eslint.config.mts`, `knip.ts`, `.secretlintrc.json`, `lefthook.yml`).
65+
`vitest.config.ts`, e2e-каркас (Playwright или Cypress), роуты React Router и конфиги
66+
`arui-presets-lint` (`eslint.config.mts`, `knip.ts`, `.secretlintrc.json`, `lefthook.yml`).
6667

6768
Дальнейшая сборка и запуск через команды `arui-scripts` в созданном проекте.
6869
См. [документацию arui-scripts](../arui-scripts/README.md).

packages/create-arui-scripts-app/src/__tests__/build-context.tests.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const base: InitAnswers = {
88
codeLoader: 'swc',
99
testRunner: 'jest',
1010
e2eFramework: 'none',
11+
useRouter: false,
1112
cssModules: true,
1213
clientServerPort: 8080,
1314
serverPort: 3000,
@@ -108,4 +109,13 @@ describe('buildContext', () => {
108109
expect(cypressCtx.devDependencies).toHaveProperty('cypress');
109110
expect(cypressCtx.devDependencies).not.toHaveProperty('@playwright/test');
110111
});
112+
113+
it('useRouter добавляет react-router и react-router-dom', () => {
114+
const withRouter = buildContext({ ...base, useRouter: true }, '1.0.0');
115+
const withoutRouter = buildContext({ ...base, useRouter: false }, '1.0.0');
116+
117+
expect(withRouter.dependencies).toHaveProperty('react-router-dom');
118+
expect(withRouter.dependencies).toHaveProperty('react-router');
119+
expect(withoutRouter.dependencies).not.toHaveProperty('react-router-dom');
120+
});
111121
});

packages/create-arui-scripts-app/src/__tests__/build-file-map.tests.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const base: InitAnswers = {
99
codeLoader: 'swc',
1010
testRunner: 'jest',
1111
e2eFramework: 'none',
12+
useRouter: false,
1213
cssModules: true,
1314
clientServerPort: 8080,
1415
serverPort: 3000,
@@ -310,4 +311,31 @@ describe('buildFileMap', () => {
310311
expect(pkg.scripts.e2e).toBeUndefined();
311312
expect(files['README.md']).not.toContain('## E2E');
312313
});
314+
315+
it('useRouter создает routes, layout, pages и BrowserRouter/StaticRouter', () => {
316+
const files = map({ useRouter: true, clientOnly: false });
317+
const pkg = JSON.parse(files['package.json']) as {
318+
dependencies: Record<string, string>;
319+
};
320+
321+
expect(files['src/client/routes.tsx']).toContain('AppRoutes');
322+
expect(files['src/client/components/layout.tsx']).toContain('Outlet');
323+
expect(files['src/client/pages/home.tsx']).toContain('HomePage');
324+
expect(files['src/client/pages/about.tsx']).toContain('AboutPage');
325+
expect(files['src/client/components/app.tsx']).toContain('AppRoutes');
326+
expect(files['src/client/index.tsx']).toContain('BrowserRouter');
327+
expect(files['src/server/index.tsx']).toContain('StaticRouter');
328+
expect(files['src/server/index.tsx']).toContain('/{path*}');
329+
expect(files['README.md']).toContain('/about');
330+
expect(pkg.dependencies).toHaveProperty('react-router-dom');
331+
});
332+
333+
it('без useRouter не создает routes/pages', () => {
334+
const files = map({ useRouter: false });
335+
336+
expect(files['src/client/routes.tsx']).toBeUndefined();
337+
expect(files['src/client/pages/home.tsx']).toBeUndefined();
338+
expect(files['src/client/index.tsx']).not.toContain('BrowserRouter');
339+
expect(files['package.json']).not.toContain('react-router-dom');
340+
});
313341
});

packages/create-arui-scripts-app/src/__tests__/create-program.tests.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ describe('createProgram', () => {
2222
});
2323

2424
it('позитивные флаги дают true', async () => {
25-
const flags = await parseFlags(['--rtk', '--css-modules', '--lint', '--install']);
25+
const flags = await parseFlags([
26+
'--rtk',
27+
'--css-modules',
28+
'--lint',
29+
'--install',
30+
'--router',
31+
]);
2632

2733
expect(flags.useRtk).toBe(true);
2834
expect(flags.cssModules).toBe(true);
2935
expect(flags.useLint).toBe(true);
3036
expect(flags.install).toBe(true);
37+
expect(flags.useRouter).toBe(true);
3138
});
3239

3340
it('--no-* флаги дают false', async () => {
@@ -36,6 +43,7 @@ describe('createProgram', () => {
3643
'--no-css-modules',
3744
'--no-polyfills',
3845
'--no-react-compiler',
46+
'--no-router',
3947
'--no-lint',
4048
'--no-install',
4149
]);
@@ -44,6 +52,7 @@ describe('createProgram', () => {
4452
expect(flags.cssModules).toBe(false);
4553
expect(flags.polyfills).toBe(false);
4654
expect(flags.reactCompiler).toBe(false);
55+
expect(flags.useRouter).toBe(false);
4756
expect(flags.useLint).toBe(false);
4857
expect(flags.install).toBe(false);
4958
});
@@ -66,4 +75,9 @@ describe('createProgram', () => {
6675
e2eFramework: 'none',
6776
});
6877
});
78+
79+
it('--router и --no-router управляют useRouter', async () => {
80+
await expect(parseFlags(['--router'])).resolves.toEqual({ useRouter: true });
81+
await expect(parseFlags(['--no-router'])).resolves.toEqual({ useRouter: false });
82+
});
6983
});

packages/create-arui-scripts-app/src/__tests__/questions.tests.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ describe('getQuestions', () => {
1212
'swc', // codeLoader
1313
'jest', // testRunner
1414
'none', // e2eFramework
15+
false, // useRouter
1516
true, // cssModules
1617
8080, // clientServerPort
1718
// serverPort пропущен
@@ -37,6 +38,7 @@ describe('getQuestions', () => {
3738
'swc', // codeLoader
3839
'jest', // testRunner
3940
'none', // e2eFramework
41+
false, // useRouter
4042
true, // cssModules
4143
8080, // clientServerPort
4244
3001, // serverPort
@@ -61,6 +63,7 @@ describe('getQuestions', () => {
6163
'app', // name
6264
false, // useRtk
6365
'none', // e2eFramework
66+
false, // useRouter
6467
true, // cssModules
6568
8080, // clientServerPort
6669
'', // dockerRegistry
@@ -91,6 +94,7 @@ describe('getQuestions', () => {
9194
'swc', // codeLoader
9295
'jest', // testRunner
9396
// e2eFramework задан флагом
97+
false, // useRouter
9498
true, // cssModules
9599
8080, // clientServerPort
96100
'', // dockerRegistry
@@ -114,6 +118,7 @@ describe('getQuestions', () => {
114118
'swc', // codeLoader
115119
'jest', // testRunner
116120
'cypress', // e2eFramework
121+
false, // useRouter
117122
true, // cssModules
118123
8080, // clientServerPort
119124
// serverPort задан флагом — вопрос пропущен

packages/create-arui-scripts-app/src/__tests__/run.tests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ describe('runInit', () => {
111111
'babel', // codeLoader
112112
'vitest', // testRunner
113113
'none', // e2eFramework
114+
false, // useRouter
114115
true, // cssModules
115116
8081, // clientServerPort
116117
'', // dockerRegistry

packages/create-arui-scripts-app/src/build-context.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const VERSIONS = {
2424
vitest: '^4.1.5',
2525
playwrightTest: '^1.57.0',
2626
cypress: '^15.19.0',
27+
reactRouterDom: '^7.6.0',
2728
aruiPresetsLint: '^11.0.0',
2829
} as const;
2930

@@ -56,6 +57,11 @@ export function buildContext(answers: InitAnswers, aruiScriptsVersion: string):
5657
dependencies['react-redux'] = VERSIONS.reactRedux;
5758
}
5859

60+
if (answers.useRouter) {
61+
dependencies['react-router'] = VERSIONS.reactRouterDom;
62+
dependencies['react-router-dom'] = VERSIONS.reactRouterDom;
63+
}
64+
5965
if (answers.polyfills) {
6066
dependencies['core-js'] = VERSIONS.coreJs;
6167
}
@@ -89,6 +95,7 @@ export function buildContext(answers: InitAnswers, aruiScriptsVersion: string):
8995
codeLoader: answers.codeLoader,
9096
testRunner: answers.testRunner,
9197
e2eFramework: answers.e2eFramework,
98+
useRouter: answers.useRouter,
9299
cssModules: answers.cssModules,
93100
clientServerPort: answers.clientServerPort,
94101
serverPort: answers.serverPort,

packages/create-arui-scripts-app/src/build-file-map.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ import {
2828
yarnrcTemplate,
2929
} from './templates/misc.template';
3030
import { packageJsonTemplate } from './templates/package-json.template';
31+
import {
32+
aboutPageTemplate,
33+
homePageTemplate,
34+
layoutTemplate,
35+
routesTemplate,
36+
} from './templates/router.template';
3137
import { serverEntryTemplate } from './templates/server-entry.template';
3238
import {
3339
counterSliceTemplate,
@@ -77,6 +83,13 @@ export function buildFileMap(ctx: TemplateContext): Record<string, string> {
7783
files[`${client}/store/counter-slice.ts`] = counterSliceTemplate();
7884
}
7985

86+
if (ctx.useRouter) {
87+
files[`${client}/routes.tsx`] = routesTemplate();
88+
files[`${client}/components/layout.tsx`] = layoutTemplate(ctx);
89+
files[`${client}/pages/home.tsx`] = homePageTemplate(ctx);
90+
files[`${client}/pages/about.tsx`] = aboutPageTemplate(ctx);
91+
}
92+
8093
if (ctx.useLint) {
8194
files['eslint.config.mts'] = eslintConfigTemplate();
8295
files['knip.ts'] = knipConfigTemplate();

packages/create-arui-scripts-app/src/create-program.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export function createProgram(onInit: InitHandler = defaultInitHandler): Command
3636
'none',
3737
]),
3838
)
39+
.option('--router', 'Подключить React Router')
40+
.option('--no-router', 'Без React Router')
3941
.option('--css-modules', 'CSS-модули')
4042
.option('--no-css-modules', 'Обычный css')
4143
.option('--client-port <port>', 'Порт dev-сервера', (value) => Number(value))
@@ -124,6 +126,10 @@ export function mapOptsToFlags(opts: Record<string, unknown>): CliFlags {
124126
flags.reactCompiler = opts.reactCompiler;
125127
}
126128

129+
if (typeof opts.router === 'boolean') {
130+
flags.useRouter = opts.router;
131+
}
132+
127133
if (typeof opts.lint === 'boolean') {
128134
flags.useLint = opts.lint;
129135
}

packages/create-arui-scripts-app/src/defaults.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type CliFlags = {
99
codeLoader?: CodeLoader;
1010
testRunner?: TestRunner;
1111
e2eFramework?: E2eFramework;
12+
useRouter?: boolean;
1213
cssModules?: boolean;
1314
clientServerPort?: number;
1415
serverPort?: number;
@@ -28,6 +29,7 @@ export function defaultAnswers(name: string): InitAnswers {
2829
codeLoader: 'swc',
2930
testRunner: 'jest',
3031
e2eFramework: 'none',
32+
useRouter: false,
3133
cssModules: true,
3234
clientServerPort: 8080,
3335
serverPort: 3000,
@@ -47,6 +49,7 @@ const ANSWER_FLAG_KEYS: Array<keyof CliFlags> = [
4749
'codeLoader',
4850
'testRunner',
4951
'e2eFramework',
52+
'useRouter',
5053
'cssModules',
5154
'clientServerPort',
5255
'serverPort',
@@ -73,6 +76,7 @@ export function answersFromFlags(defaultName: string, flags: CliFlags): InitAnsw
7376
...(flags.codeLoader === undefined ? {} : { codeLoader: flags.codeLoader }),
7477
...(flags.testRunner === undefined ? {} : { testRunner: flags.testRunner }),
7578
...(flags.e2eFramework === undefined ? {} : { e2eFramework: flags.e2eFramework }),
79+
...(flags.useRouter === undefined ? {} : { useRouter: flags.useRouter }),
7680
...(flags.cssModules === undefined ? {} : { cssModules: flags.cssModules }),
7781
...(flags.clientServerPort === undefined
7882
? {}

0 commit comments

Comments
 (0)