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.
- package/CHANGELOG.md +19 -0
- package/README.md +15 -3
- package/assets/.yarn/releases/yarn-4.9.1.cjs +0 -0
- package/build/build-context.js +20 -0
- package/build/build-file-map.js +29 -2
- package/build/create-program.js +18 -0
- package/build/defaults.d.ts +4 -1
- package/build/defaults.js +8 -2
- package/build/install-dependencies.d.ts +2 -0
- package/build/install-dependencies.js +18 -5
- package/build/questions.js +31 -0
- package/build/run.js +40 -0
- package/build/templates/app-component.template.js +20 -7
- package/build/templates/app-styles.template.js +43 -12
- package/build/templates/arui-scripts-config.template.js +2 -2
- package/build/templates/client-entry.template.js +28 -18
- package/build/templates/e2e-cypress.template.d.ts +5 -0
- package/build/templates/e2e-cypress.template.js +52 -0
- package/build/templates/e2e-playwright.template.d.ts +4 -0
- package/build/templates/e2e-playwright.template.js +56 -0
- package/build/templates/lint.template.d.ts +4 -0
- package/build/templates/lint.template.js +52 -0
- package/build/templates/misc.template.d.ts +1 -1
- package/build/templates/misc.template.js +62 -5
- package/build/templates/package-json.template.js +26 -0
- package/build/templates/router.template.d.ts +5 -0
- package/build/templates/router.template.js +159 -0
- package/build/templates/server-entry.template.js +37 -41
- package/build/templates/store.template.js +1 -1
- package/build/templates/tsconfig.template.js +9 -9
- package/build/types.d.ts +7 -0
- package/build/versions.d.ts +1 -1
- package/build/versions.js +1 -1
- 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
|
-
|
|
7
|
-
const NextApp = require('./components/app').App;
|
|
6
|
+
const mod = require('./components/app') as { App: typeof App };
|
|
8
7
|
|
|
9
|
-
render(
|
|
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!,
|
|
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,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,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
|
+
}
|