create-arui-scripts-app 1.0.0 → 1.2.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 (34) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +15 -3
  3. package/assets/.yarn/releases/yarn-4.9.1.cjs +0 -0
  4. package/build/build-context.js +20 -0
  5. package/build/build-file-map.js +29 -2
  6. package/build/create-program.js +18 -0
  7. package/build/defaults.d.ts +4 -1
  8. package/build/defaults.js +8 -2
  9. package/build/install-dependencies.d.ts +2 -0
  10. package/build/install-dependencies.js +18 -5
  11. package/build/questions.js +31 -0
  12. package/build/run.js +40 -0
  13. package/build/templates/app-component.template.js +20 -7
  14. package/build/templates/app-styles.template.js +43 -12
  15. package/build/templates/arui-scripts-config.template.js +2 -2
  16. package/build/templates/client-entry.template.js +28 -18
  17. package/build/templates/e2e-cypress.template.d.ts +5 -0
  18. package/build/templates/e2e-cypress.template.js +52 -0
  19. package/build/templates/e2e-playwright.template.d.ts +4 -0
  20. package/build/templates/e2e-playwright.template.js +56 -0
  21. package/build/templates/lint.template.d.ts +4 -0
  22. package/build/templates/lint.template.js +52 -0
  23. package/build/templates/misc.template.d.ts +1 -1
  24. package/build/templates/misc.template.js +62 -5
  25. package/build/templates/package-json.template.js +26 -0
  26. package/build/templates/router.template.d.ts +5 -0
  27. package/build/templates/router.template.js +159 -0
  28. package/build/templates/server-entry.template.js +37 -41
  29. package/build/templates/store.template.js +1 -1
  30. package/build/templates/tsconfig.template.js +9 -9
  31. package/build/types.d.ts +7 -0
  32. package/build/versions.d.ts +1 -1
  33. package/build/versions.js +1 -1
  34. package/package.json +55 -55
@@ -3,21 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.clientEntryTemplate = clientEntryTemplate;
4
4
  const HMR_BLOCK = `if (process.env.NODE_ENV !== 'production' && module.hot) {
5
5
  module.hot.accept('./components/app', () => {
6
- // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
7
- const NextApp = require('./components/app').App;
6
+ const mod = require('./components/app') as { App: typeof App };
8
7
 
9
- render(NextApp);
8
+ render(mod.App);
10
9
  });
11
10
  }
12
11
  `;
12
+ function wrapWithRouter(jsx, ctx) {
13
+ if (!ctx.useRouter) {
14
+ return jsx;
15
+ }
16
+ return `<BrowserRouter>${jsx}</BrowserRouter>`;
17
+ }
13
18
  function clientEntryTemplate(ctx) {
14
19
  return ctx.clientOnly ? clientOnlyEntryTemplate(ctx) : ssrEntryTemplate(ctx);
15
20
  }
21
+ function routerImport(ctx) {
22
+ return ctx.useRouter ? "\nimport { BrowserRouter } from 'react-router-dom';" : '';
23
+ }
16
24
  function ssrEntryTemplate(ctx) {
17
25
  if (ctx.useRtk) {
26
+ const appTree = wrapWithRouter(`
27
+ <Provider store={store}>
28
+ <App />
29
+ </Provider>`, ctx);
18
30
  return `import React from 'react';
19
31
  import { hydrateRoot } from 'react-dom/client';
20
- import { Provider } from 'react-redux';
32
+ import { Provider } from 'react-redux';${routerImport(ctx)}
21
33
 
22
34
  import { App } from './components/app';
23
35
  import { makeStore, type RootState } from './store';
@@ -32,32 +44,30 @@ const store = makeStore(window.__PRELOADED_STATE__);
32
44
  const targetElement = document.getElementById('react-app');
33
45
 
34
46
  const root = hydrateRoot(
35
- targetElement!,
36
- <Provider store={store}>
37
- <App />
38
- </Provider>,
47
+ targetElement!,${appTree},
39
48
  );
40
49
 
41
50
  function render(AppComponent: typeof App) {
42
- root.render(
51
+ root.render(${wrapWithRouter(`
43
52
  <Provider store={store}>
44
53
  <AppComponent />
45
- </Provider>,
54
+ </Provider>`, ctx)},
46
55
  );
47
56
  }
48
57
 
49
58
  ${HMR_BLOCK}`;
50
59
  }
60
+ const appTree = wrapWithRouter('<App />', ctx);
51
61
  return `import React from 'react';
52
- import { hydrateRoot } from 'react-dom/client';
62
+ import { hydrateRoot } from 'react-dom/client';${routerImport(ctx)}
53
63
 
54
64
  import { App } from './components/app';
55
65
 
56
66
  const targetElement = document.getElementById('react-app');
57
- const root = hydrateRoot(targetElement!, <App />);
67
+ const root = hydrateRoot(targetElement!, ${appTree});
58
68
 
59
69
  function render(AppComponent: typeof App) {
60
- root.render(<AppComponent />);
70
+ root.render(${wrapWithRouter('<AppComponent />', ctx)});
61
71
  }
62
72
 
63
73
  ${HMR_BLOCK}`;
@@ -66,7 +76,7 @@ function clientOnlyEntryTemplate(ctx) {
66
76
  if (ctx.useRtk) {
67
77
  return `import React from 'react';
68
78
  import { createRoot } from 'react-dom/client';
69
- import { Provider } from 'react-redux';
79
+ import { Provider } from 'react-redux';${routerImport(ctx)}
70
80
 
71
81
  import { App } from './components/app';
72
82
  import { makeStore } from './store';
@@ -76,10 +86,10 @@ const targetElement = document.getElementById('react-app');
76
86
  const root = createRoot(targetElement!);
77
87
 
78
88
  function render(AppComponent: typeof App) {
79
- root.render(
89
+ root.render(${wrapWithRouter(`
80
90
  <Provider store={store}>
81
91
  <AppComponent />
82
- </Provider>,
92
+ </Provider>`, ctx)},
83
93
  );
84
94
  }
85
95
 
@@ -88,7 +98,7 @@ render(App);
88
98
  ${HMR_BLOCK}`;
89
99
  }
90
100
  return `import React from 'react';
91
- import { createRoot } from 'react-dom/client';
101
+ import { createRoot } from 'react-dom/client';${routerImport(ctx)}
92
102
 
93
103
  import { App } from './components/app';
94
104
 
@@ -96,7 +106,7 @@ const targetElement = document.getElementById('react-app');
96
106
  const root = createRoot(targetElement!);
97
107
 
98
108
  function render(AppComponent: typeof App) {
99
- root.render(<AppComponent />);
109
+ root.render(${wrapWithRouter('<AppComponent />', ctx)});
100
110
  }
101
111
 
102
112
  render(App);
@@ -0,0 +1,5 @@
1
+ import { type TemplateContext } from '../types';
2
+ export declare function cypressConfigTemplate(ctx: TemplateContext): string;
3
+ export declare function cypressSupportE2eTemplate(): string;
4
+ export declare function cypressSupportCommandsTemplate(): string;
5
+ export declare function cypressExampleSpecTemplate(): string;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cypressConfigTemplate = cypressConfigTemplate;
4
+ exports.cypressSupportE2eTemplate = cypressSupportE2eTemplate;
5
+ exports.cypressSupportCommandsTemplate = cypressSupportCommandsTemplate;
6
+ exports.cypressExampleSpecTemplate = cypressExampleSpecTemplate;
7
+ function baseUrl(ctx) {
8
+ return `http://localhost:${ctx.clientServerPort}`;
9
+ }
10
+ function cypressConfigTemplate(ctx) {
11
+ return `import { defineConfig } from 'cypress';
12
+
13
+ export default defineConfig({
14
+ video: false,
15
+ e2e: {
16
+ baseUrl: '${baseUrl(ctx)}',
17
+ specPattern: 'cypress/e2e/**/*.cy.{ts,tsx}',
18
+ supportFile: 'cypress/support/e2e.ts',
19
+ },
20
+ });
21
+ `;
22
+ }
23
+ function cypressSupportE2eTemplate() {
24
+ return `import './commands';
25
+ `;
26
+ }
27
+ function cypressSupportCommandsTemplate() {
28
+ return `// Кастомные команды Cypress.
29
+ // Пример:
30
+ // Cypress.Commands.add('login', (email, password) => { ... })
31
+
32
+ export {};
33
+
34
+ declare global {
35
+ // eslint-disable-next-line @typescript-eslint/no-namespace
36
+ namespace Cypress {
37
+ interface Chainable {
38
+ // login(email: string, password: string): Chainable<void>;
39
+ }
40
+ }
41
+ }
42
+ `;
43
+ }
44
+ function cypressExampleSpecTemplate() {
45
+ return `describe('home page', () => {
46
+ it('loads', () => {
47
+ cy.visit('/');
48
+ cy.get('body').should('be.visible');
49
+ });
50
+ });
51
+ `;
52
+ }
@@ -0,0 +1,4 @@
1
+ import { type TemplateContext } from '../types';
2
+ export declare function playwrightConfigTemplate(ctx: TemplateContext): string;
3
+ export declare function playwrightHelpersTemplate(): string;
4
+ export declare function playwrightExampleSpecTemplate(): string;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.playwrightConfigTemplate = playwrightConfigTemplate;
4
+ exports.playwrightHelpersTemplate = playwrightHelpersTemplate;
5
+ exports.playwrightExampleSpecTemplate = playwrightExampleSpecTemplate;
6
+ function baseUrl(ctx) {
7
+ return `http://localhost:${ctx.clientServerPort}`;
8
+ }
9
+ function playwrightConfigTemplate(ctx) {
10
+ const url = baseUrl(ctx);
11
+ return `import { defineConfig, devices } from '@playwright/test';
12
+
13
+ export default defineConfig({
14
+ testDir: './e2e',
15
+ fullyParallel: true,
16
+ forbidOnly: !!process.env.CI,
17
+ retries: process.env.CI ? 2 : 0,
18
+ workers: process.env.CI ? 1 : undefined,
19
+ reporter: 'html',
20
+ use: {
21
+ baseURL: '${url}',
22
+ trace: 'on-first-retry',
23
+ },
24
+ projects: [
25
+ {
26
+ name: 'chromium',
27
+ use: { ...devices['Desktop Chrome'] },
28
+ },
29
+ ],
30
+ webServer: {
31
+ command: 'yarn start',
32
+ url: '${url}',
33
+ reuseExistingServer: !process.env.CI,
34
+ },
35
+ });
36
+ `;
37
+ }
38
+ function playwrightHelpersTemplate() {
39
+ return `import { type Page } from '@playwright/test';
40
+
41
+ export async function gotoHome(page: Page): Promise<void> {
42
+ await page.goto('/');
43
+ }
44
+ `;
45
+ }
46
+ function playwrightExampleSpecTemplate() {
47
+ return `import { expect, test } from '@playwright/test';
48
+
49
+ import { gotoHome } from './helpers';
50
+
51
+ test('home page loads', async ({ page }) => {
52
+ await gotoHome(page);
53
+ await expect(page.locator('body')).toBeVisible();
54
+ });
55
+ `;
56
+ }
@@ -0,0 +1,4 @@
1
+ export declare function eslintConfigTemplate(): string;
2
+ export declare function knipConfigTemplate(): string;
3
+ export declare function secretlintConfigTemplate(): string;
4
+ export declare function lefthookConfigTemplate(): string;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eslintConfigTemplate = eslintConfigTemplate;
4
+ exports.knipConfigTemplate = knipConfigTemplate;
5
+ exports.secretlintConfigTemplate = secretlintConfigTemplate;
6
+ exports.lefthookConfigTemplate = lefthookConfigTemplate;
7
+ function eslintConfigTemplate() {
8
+ return `import { defineConfig, eslintConfig, TYPESCRIPT_SCRIPTS_SCOPE } from 'arui-presets-lint/eslint';
9
+
10
+ export default defineConfig(eslintConfig, [
11
+ {
12
+ languageOptions: {
13
+ parserOptions: {
14
+ projectService: {
15
+ allowDefaultProject: [
16
+ 'arui-scripts.config.ts',
17
+ 'eslint.config.mts',
18
+ 'knip.ts',
19
+ 'vitest.config.ts',
20
+ ],
21
+ },
22
+ },
23
+ },
24
+ files: [TYPESCRIPT_SCRIPTS_SCOPE],
25
+ },
26
+ ]);
27
+ `;
28
+ }
29
+ function knipConfigTemplate() {
30
+ return `import baseConfig from 'arui-presets-lint/knip';
31
+
32
+ export default {
33
+ ...baseConfig,
34
+ // ts-jest подключается через jest.preset arui-scripts, прямых импортов в коде нет
35
+ ignoreDependencies: [...baseConfig.ignoreDependencies, 'ts-jest'],
36
+ };
37
+ `;
38
+ }
39
+ function secretlintConfigTemplate() {
40
+ return `${JSON.stringify({
41
+ rules: [
42
+ {
43
+ id: '@secretlint/secretlint-rule-preset-recommend',
44
+ },
45
+ ],
46
+ }, null, 4)}\n`;
47
+ }
48
+ function lefthookConfigTemplate() {
49
+ return `extends:
50
+ - ./node_modules/arui-presets-lint/lefthook/index.yml
51
+ `;
52
+ }
@@ -1,5 +1,5 @@
1
1
  import { type TemplateContext } from '../types';
2
- export declare function gitignoreTemplate(): string;
2
+ export declare function gitignoreTemplate(ctx?: TemplateContext): string;
3
3
  export declare function globalDefinitionsTemplate(): string;
4
4
  export declare function polyfillsTemplate(): string;
5
5
  export declare function yarnrcTemplate(): string;
@@ -5,7 +5,23 @@ exports.globalDefinitionsTemplate = globalDefinitionsTemplate;
5
5
  exports.polyfillsTemplate = polyfillsTemplate;
6
6
  exports.yarnrcTemplate = yarnrcTemplate;
7
7
  exports.readmeTemplate = readmeTemplate;
8
- function gitignoreTemplate() {
8
+ function gitignoreTemplate(ctx) {
9
+ let e2eIgnores = '';
10
+ if ((ctx === null || ctx === void 0 ? void 0 : ctx.e2eFramework) === 'playwright') {
11
+ e2eIgnores = `
12
+ test-results/
13
+ playwright-report/
14
+ blob-report/
15
+ playwright/.cache/
16
+ `;
17
+ }
18
+ else if ((ctx === null || ctx === void 0 ? void 0 : ctx.e2eFramework) === 'cypress') {
19
+ e2eIgnores = `
20
+ cypress/videos/
21
+ cypress/screenshots/
22
+ cypress/downloads/
23
+ `;
24
+ }
9
25
  return `node_modules
10
26
  .build
11
27
  build.tar
@@ -18,7 +34,7 @@ build.tar
18
34
  !.yarn/releases
19
35
  !.yarn/sdks
20
36
  !.yarn/versions
21
- `;
37
+ ${e2eIgnores}`;
22
38
  }
23
39
  function globalDefinitionsTemplate() {
24
40
  return `declare module '*.png' {
@@ -57,6 +73,48 @@ defaultSemverRangePrefix: ""
57
73
  }
58
74
  function readmeTemplate(ctx) {
59
75
  const testCommand = ctx.testRunner === 'jest' ? 'arui-scripts test' : 'arui-scripts test:vitest';
76
+ const lintSection = ctx.useLint
77
+ ? `
78
+ yarn lint - eslint, stylelint, prettier, knip, secretlint
79
+ yarn lint:fix - автофикс eslint/stylelint + format
80
+ yarn format - prettier
81
+ `
82
+ : '';
83
+ let e2eSection = '';
84
+ if (ctx.e2eFramework === 'playwright') {
85
+ e2eSection = `
86
+ ## E2E (Playwright)
87
+
88
+ Перед первым запуском установите браузеры:
89
+
90
+ yarn playwright install
91
+
92
+ Команды:
93
+
94
+ yarn e2e - headless-прогон
95
+ yarn e2e:ui - UI-режим Playwright
96
+
97
+ Dev-сервер поднимается автоматически через webServer в playwright.config.ts.
98
+ `;
99
+ }
100
+ else if (ctx.e2eFramework === 'cypress') {
101
+ e2eSection = `
102
+ ## E2E (Cypress)
103
+
104
+ Сначала запустите приложение (\`yarn start\`), затем в другом терминале:
105
+
106
+ yarn e2e - headless-прогон
107
+ yarn e2e:open - интерактивный Cypress
108
+ `;
109
+ }
110
+ const routerSection = ctx.useRouter
111
+ ? `
112
+ ## Маршруты
113
+
114
+ / - главная (Home)
115
+ /about - о проекте (About)
116
+ `
117
+ : '';
60
118
  return `# ${ctx.name}
61
119
 
62
120
  Проект создан с помощью arui-scripts.
@@ -65,10 +123,9 @@ function readmeTemplate(ctx) {
65
123
 
66
124
  arui-scripts start - запуск dev-сервера
67
125
  arui-scripts build - production-сборка
68
- ${testCommand}${' '.repeat(Math.max(1, 20 - testCommand.length))}- запуск тестов
69
-
126
+ ${testCommand}${' '.repeat(Math.max(1, 20 - testCommand.length))}- запуск тестов${lintSection}
70
127
  ## Установка
71
128
 
72
129
  yarn install
73
- `;
130
+ ${routerSection}${e2eSection}`;
74
131
  }
@@ -18,6 +18,27 @@ function packageJsonTemplate(ctx) {
18
18
  if (ctx.dockerRegistry) {
19
19
  scripts['docker-build'] = 'arui-scripts docker-build';
20
20
  }
21
+ if (ctx.useLint) {
22
+ scripts['lint:styles'] = 'arui-presets-lint styles --max-warnings=0';
23
+ scripts['lint:scripts'] = 'arui-presets-lint scripts --max-warnings=0';
24
+ scripts.format = 'arui-presets-lint format';
25
+ scripts['format:check'] = 'arui-presets-lint format:check';
26
+ scripts['lint:unused'] = 'arui-presets-lint knip';
27
+ scripts['lint:unused:fix'] = 'arui-presets-lint knip --fix';
28
+ scripts['lint:secrets'] = 'arui-presets-lint secretlint';
29
+ scripts.lint =
30
+ 'yarn lint:styles && yarn lint:scripts && yarn format:check && yarn lint:unused && yarn lint:secrets';
31
+ scripts['lint:fix'] =
32
+ 'yarn lint:styles --fix && yarn lint:scripts --fix && yarn format && yarn lint:secrets';
33
+ }
34
+ if (ctx.e2eFramework === 'playwright') {
35
+ scripts.e2e = 'playwright test';
36
+ scripts['e2e:ui'] = 'playwright test --ui';
37
+ }
38
+ else if (ctx.e2eFramework === 'cypress') {
39
+ scripts.e2e = 'cypress run';
40
+ scripts['e2e:open'] = 'cypress open';
41
+ }
21
42
  const pkg = {
22
43
  name: ctx.name,
23
44
  version: '0.1.0',
@@ -27,6 +48,11 @@ function packageJsonTemplate(ctx) {
27
48
  node: '>=24.11.1',
28
49
  },
29
50
  };
51
+ if (ctx.useLint) {
52
+ pkg.prettier = 'arui-presets-lint/prettier';
53
+ pkg.stylelint = { extends: 'arui-presets-lint/stylelint' };
54
+ pkg.commitlint = { extends: './node_modules/arui-presets-lint/commitlint' };
55
+ }
30
56
  if (ctx.testRunner === 'jest') {
31
57
  pkg.jest = { preset: 'arui-scripts' };
32
58
  }
@@ -0,0 +1,5 @@
1
+ import { type TemplateContext } from '../types';
2
+ export declare function layoutTemplate(ctx: TemplateContext): string;
3
+ export declare function homePageTemplate(ctx: TemplateContext): string;
4
+ export declare function aboutPageTemplate(ctx: TemplateContext): string;
5
+ export declare function routesTemplate(): string;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.layoutTemplate = layoutTemplate;
4
+ exports.homePageTemplate = homePageTemplate;
5
+ exports.aboutPageTemplate = aboutPageTemplate;
6
+ exports.routesTemplate = routesTemplate;
7
+ function tsString(value) {
8
+ return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
9
+ }
10
+ function layoutTemplate(ctx) {
11
+ const styleImport = ctx.cssModules
12
+ ? "import styles from './app.module.css';"
13
+ : "import './app.css';";
14
+ const navClass = ctx.cssModules ? '{styles.nav}' : "'app__nav'";
15
+ const linkClass = ctx.cssModules ? '{styles.navLink}' : "'app__nav-link'";
16
+ return `import React from 'react';
17
+ import { Link, Outlet } from 'react-router-dom';
18
+
19
+ ${styleImport}
20
+
21
+ export function Layout() {
22
+ return (
23
+ <div>
24
+ <nav className=${navClass}>
25
+ <Link to='/' className=${linkClass}>
26
+ Главная
27
+ </Link>
28
+ <span aria-hidden={true}> · </span>
29
+ <Link to='/about' className=${linkClass}>
30
+ О проекте
31
+ </Link>
32
+ </nav>
33
+ <Outlet />
34
+ </div>
35
+ );
36
+ }
37
+ `;
38
+ }
39
+ function homePageTemplate(ctx) {
40
+ const styleImport = ctx.cssModules
41
+ ? "import styles from '../components/app.module.css';"
42
+ : "import '../components/app.css';";
43
+ const rootClass = ctx.cssModules ? '{styles.root}' : "'app'";
44
+ const titleClass = ctx.cssModules ? '{styles.title}' : "'app__title'";
45
+ const appNameLiteral = tsString(ctx.name);
46
+ const coreImports = `import { Button } from '@alfalab/core-components/button';
47
+ import { Gap } from '@alfalab/core-components/gap';
48
+ import { Typography } from '@alfalab/core-components/typography';`;
49
+ if (ctx.useRtk) {
50
+ return `import React from 'react';
51
+
52
+ ${coreImports}
53
+
54
+ import { decrement, increment } from '../store/counter-slice';
55
+ import { useAppDispatch, useAppSelector } from '../store/hooks';
56
+
57
+ ${styleImport}
58
+
59
+ const appName = ${appNameLiteral};
60
+
61
+ export function HomePage() {
62
+ const count = useAppSelector((state) => state.counter.value);
63
+ const dispatch = useAppDispatch();
64
+
65
+ return (
66
+ <div className=${rootClass}>
67
+ <Typography.Title view='medium' tag='h1' className=${titleClass}>
68
+ {appName}
69
+ </Typography.Title>
70
+ <Gap size={16} />
71
+ <Typography.Text view='primary-medium'>Счетчик: {count}</Typography.Text>
72
+ <Gap size={16} />
73
+ <Button view='accent' size={48} onClick={() => dispatch(increment())}>
74
+ +1
75
+ </Button>{' '}
76
+ <Button view='secondary' size={48} onClick={() => dispatch(decrement())}>
77
+ -1
78
+ </Button>
79
+ </div>
80
+ );
81
+ }
82
+ `;
83
+ }
84
+ return `import React, { useState } from 'react';
85
+
86
+ ${coreImports}
87
+
88
+ ${styleImport}
89
+
90
+ const appName = ${appNameLiteral};
91
+
92
+ export function HomePage() {
93
+ const [count, setCount] = useState(0);
94
+
95
+ return (
96
+ <div className=${rootClass}>
97
+ <Typography.Title view='medium' tag='h1' className=${titleClass}>
98
+ {appName}
99
+ </Typography.Title>
100
+ <Gap size={16} />
101
+ <Typography.Text view='primary-medium'>Счетчик: {count}</Typography.Text>
102
+ <Gap size={16} />
103
+ <Button view='accent' size={48} onClick={() => setCount((prev) => prev + 1)}>
104
+ +1
105
+ </Button>{' '}
106
+ <Button view='secondary' size={48} onClick={() => setCount((prev) => prev - 1)}>
107
+ -1
108
+ </Button>
109
+ </div>
110
+ );
111
+ }
112
+ `;
113
+ }
114
+ function aboutPageTemplate(ctx) {
115
+ const styleImport = ctx.cssModules
116
+ ? "import styles from '../components/app.module.css';"
117
+ : "import '../components/app.css';";
118
+ const rootClass = ctx.cssModules ? '{styles.root}' : "'app'";
119
+ const titleClass = ctx.cssModules ? '{styles.title}' : "'app__title'";
120
+ return `import React from 'react';
121
+
122
+ import { Typography } from '@alfalab/core-components/typography';
123
+
124
+ ${styleImport}
125
+
126
+ export function AboutPage() {
127
+ return (
128
+ <div className=${rootClass}>
129
+ <Typography.Title view='medium' tag='h1' className=${titleClass}>
130
+ О проекте
131
+ </Typography.Title>
132
+ <Typography.Text view='primary-medium'>
133
+ Страница для React Router
134
+ </Typography.Text>
135
+ </div>
136
+ );
137
+ }
138
+ `;
139
+ }
140
+ function routesTemplate() {
141
+ return `import React from 'react';
142
+ import { Route, Routes } from 'react-router-dom';
143
+
144
+ import { Layout } from './components/layout';
145
+ import { AboutPage } from './pages/about';
146
+ import { HomePage } from './pages/home';
147
+
148
+ export function AppRoutes() {
149
+ return (
150
+ <Routes>
151
+ <Route element={<Layout />}>
152
+ <Route index={true} element={<HomePage />} />
153
+ <Route path='about' element={<AboutPage />} />
154
+ </Route>
155
+ </Routes>
156
+ );
157
+ }
158
+ `;
159
+ }