create-arui-scripts-app 1.0.0 → 1.1.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 ADDED
@@ -0,0 +1,13 @@
1
+ # create-arui-scripts-app
2
+
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#566](https://github.com/core-ds/arui-scripts/pull/566) [`d5de571`](https://github.com/core-ds/arui-scripts/commit/d5de571bf16582ad78258d3b35acbc08d7da42a4) Thanks [@dmitrbrvsk](https://github.com/dmitrbrvsk)! - добавлена возможность подключать пакет arui-presets-lint (https://github.com/core-ds/arui-presets-lint/tree/master/packages/arui-presets-lint)
8
+
9
+ ## 1.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - [#552](https://github.com/core-ds/arui-scripts/pull/552) [`3658e49`](https://github.com/core-ds/arui-scripts/commit/3658e49b0b752c592e738726aa8056569e28e19b) Thanks [@dmitrbrvsk](https://github.com/dmitrbrvsk)! - Добавлен пакет `create-arui-scripts-app` для генерации нового приложения через CLI. В `arui-scripts` CLI был переведен на commander.
package/README.md CHANGED
@@ -35,7 +35,7 @@ npx create-arui-scripts-app my-app --yes
35
35
  - `--css-modules` / `--no-css-modules` - CSS-модули
36
36
  - `--client-port` / `--server-port` - порты клиента и сервера
37
37
  - `--docker-registry` / `--presets` - docker registry и preset
38
- - `--polyfills` / `--react-compiler` / `--install` - и соответствующие `--no-*`
38
+ - `--polyfills` / `--react-compiler` / `--lint` / `--install` - и соответствующие `--no-*`
39
39
 
40
40
  ## Что настраивает мастер
41
41
 
@@ -44,15 +44,24 @@ npx create-arui-scripts-app my-app --yes
44
44
  - SSR-сервер на **Hapi** с рендерингом приложения (`renderToString`) и гидрацией на клиенте
45
45
  - транспилятор (**swc** / babel / tsc) и тест-раннер (**Jest** / Vitest)
46
46
  - CSS-модули, полифилы (`core-js`), `experimentalReactCompiler`, docker registry, preset
47
+ - опционально **arui-presets-lint** (eslint, prettier, stylelint, knip, secretlint, lefthook)
47
48
  - опциональная установка зависимостей сразу после генерации
48
49
 
50
+ Пример с линтерами:
51
+
52
+ ```bash
53
+ npx create-arui-scripts-app my-app --yes --lint --install
54
+ ```
55
+
49
56
  ## Результат
50
57
 
51
58
  Генерируются `package.json`, `arui-scripts.config.ts`, `tsconfig.json`, клиентская точка входа,
52
59
  пример компонента со стилями и тестом, `global-definitions.d.ts`, `.gitignore`, `README.md`,
53
60
  `.yarnrc.yml` (с `nodeLinker: node-modules`),
54
- а также в зависимости от ответов - серверная точка входа на Hapi, store на RTK, полифилы и
55
- `vitest.config.ts`.
61
+ а также в зависимости от ответов - серверная точка входа на Hapi, store на RTK, полифилы,
62
+ `vitest.config.ts` и конфиги `arui-presets-lint` (`eslint.config.mts`, `knip.ts`,
63
+ `.secretlintrc.json`, `lefthook.yml`).
56
64
 
57
65
  Дальнейшая сборка и запуск через команды `arui-scripts` в созданном проекте.
58
66
  См. [документацию arui-scripts](../arui-scripts/README.md).
67
+ При подключении lint: `yarn lint` / `yarn lint:fix`.
File without changes
@@ -23,6 +23,7 @@ const VERSIONS = {
23
23
  tsJest: '^29.1.0',
24
24
  typesJest: '^29.5.0',
25
25
  vitest: '^4.1.5',
26
+ aruiPresetsLint: '^11.0.0',
26
27
  };
27
28
  function buildContext(answers, aruiScriptsVersion) {
28
29
  const dependencies = {
@@ -63,6 +64,9 @@ function buildContext(answers, aruiScriptsVersion) {
63
64
  else {
64
65
  devDependencies.vitest = VERSIONS.vitest;
65
66
  }
67
+ if (answers.useLint) {
68
+ devDependencies['arui-presets-lint'] = VERSIONS.aruiPresetsLint;
69
+ }
66
70
  return {
67
71
  name: answers.name,
68
72
  useRtk: answers.useRtk,
@@ -76,6 +80,7 @@ function buildContext(answers, aruiScriptsVersion) {
76
80
  presets: answers.presets.trim(),
77
81
  polyfills: answers.polyfills,
78
82
  reactCompiler: answers.reactCompiler,
83
+ useLint: answers.useLint,
79
84
  aruiScriptsVersion,
80
85
  dependencies,
81
86
  devDependencies,
@@ -7,6 +7,7 @@ const app_styles_template_1 = require("./templates/app-styles.template");
7
7
  const app_test_template_1 = require("./templates/app-test.template");
8
8
  const arui_scripts_config_template_1 = require("./templates/arui-scripts-config.template");
9
9
  const client_entry_template_1 = require("./templates/client-entry.template");
10
+ const lint_template_1 = require("./templates/lint.template");
10
11
  const misc_template_1 = require("./templates/misc.template");
11
12
  const package_json_template_1 = require("./templates/package-json.template");
12
13
  const server_entry_template_1 = require("./templates/server-entry.template");
@@ -29,7 +30,7 @@ function buildFileMap(ctx) {
29
30
  [`${client}/index.tsx`]: (0, client_entry_template_1.clientEntryTemplate)(ctx),
30
31
  [`${client}/components/app.tsx`]: (0, app_component_template_1.appComponentTemplate)(ctx),
31
32
  [`${client}/components/${(0, app_styles_template_1.appStylesFileName)(ctx)}`]: (0, app_styles_template_1.appStylesTemplate)(ctx),
32
- [`${client}/components/__tests__/app.test.tsx`]: (0, app_test_template_1.appTestTemplate)(ctx),
33
+ [`${client}/components/__tests__/app.test.ts`]: (0, app_test_template_1.appTestTemplate)(ctx),
33
34
  };
34
35
  if (!ctx.clientOnly) {
35
36
  files['src/server/index.tsx'] = (0, server_entry_template_1.serverEntryTemplate)(ctx);
@@ -45,5 +46,11 @@ function buildFileMap(ctx) {
45
46
  files[`${client}/store/hooks.ts`] = (0, store_template_1.storeHooksTemplate)();
46
47
  files[`${client}/store/counter-slice.ts`] = (0, store_template_1.counterSliceTemplate)();
47
48
  }
49
+ if (ctx.useLint) {
50
+ files['eslint.config.mts'] = (0, lint_template_1.eslintConfigTemplate)();
51
+ files['knip.ts'] = (0, lint_template_1.knipConfigTemplate)();
52
+ files['.secretlintrc.json'] = (0, lint_template_1.secretlintConfigTemplate)();
53
+ files['lefthook.yml'] = (0, lint_template_1.lefthookConfigTemplate)();
54
+ }
48
55
  return files;
49
56
  }
@@ -41,6 +41,8 @@ function createProgram(onInit = defaultInitHandler) {
41
41
  .option('--no-polyfills', 'Без полифилов')
42
42
  .option('--react-compiler', 'Включить experimentalReactCompiler')
43
43
  .option('--no-react-compiler', 'Выключить experimentalReactCompiler')
44
+ .option('--lint', 'Подключить arui-presets-lint')
45
+ .option('--no-lint', 'Без arui-presets-lint')
44
46
  .option('--install', 'Установить зависимости после генерации')
45
47
  .option('--no-install', 'Не устанавливать зависимости')
46
48
  .showHelpAfterError('(используйте --help для справки)')
@@ -96,6 +98,9 @@ function mapOptsToFlags(opts) {
96
98
  if (typeof opts.reactCompiler === 'boolean') {
97
99
  flags.reactCompiler = opts.reactCompiler;
98
100
  }
101
+ if (typeof opts.lint === 'boolean') {
102
+ flags.useLint = opts.lint;
103
+ }
99
104
  if (typeof opts.install === 'boolean') {
100
105
  flags.install = opts.install;
101
106
  }
@@ -14,6 +14,7 @@ export type CliFlags = {
14
14
  presets?: string;
15
15
  polyfills?: boolean;
16
16
  reactCompiler?: boolean;
17
+ useLint?: boolean;
17
18
  install?: boolean;
18
19
  };
19
20
  export declare function defaultAnswers(name: string): InitAnswers;
package/build/defaults.js CHANGED
@@ -17,6 +17,7 @@ function defaultAnswers(name) {
17
17
  presets: '',
18
18
  polyfills: false,
19
19
  reactCompiler: false,
20
+ useLint: false,
20
21
  install: false,
21
22
  };
22
23
  }
@@ -33,6 +34,7 @@ const ANSWER_FLAG_KEYS = [
33
34
  'presets',
34
35
  'polyfills',
35
36
  'reactCompiler',
37
+ 'useLint',
36
38
  'install',
37
39
  ];
38
40
  function hasAnswerFlags(flags) {
@@ -42,7 +44,7 @@ function hasAnswerFlags(flags) {
42
44
  function answersFromFlags(defaultName, flags) {
43
45
  var _a, _b;
44
46
  const base = defaultAnswers(((_a = flags.name) === null || _a === void 0 ? void 0 : _a.trim()) || defaultName);
45
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, base), (flags.useRtk === undefined ? {} : { useRtk: flags.useRtk })), (flags.clientOnly === undefined ? {} : { clientOnly: flags.clientOnly })), (flags.codeLoader === undefined ? {} : { codeLoader: flags.codeLoader })), (flags.testRunner === undefined ? {} : { testRunner: flags.testRunner })), (flags.cssModules === undefined ? {} : { cssModules: flags.cssModules })), (flags.clientServerPort === undefined
47
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, base), (flags.useRtk === undefined ? {} : { useRtk: flags.useRtk })), (flags.clientOnly === undefined ? {} : { clientOnly: flags.clientOnly })), (flags.codeLoader === undefined ? {} : { codeLoader: flags.codeLoader })), (flags.testRunner === undefined ? {} : { testRunner: flags.testRunner })), (flags.cssModules === undefined ? {} : { cssModules: flags.cssModules })), (flags.clientServerPort === undefined
46
48
  ? {}
47
- : { clientServerPort: flags.clientServerPort })), (flags.serverPort === undefined ? {} : { serverPort: flags.serverPort })), (flags.dockerRegistry === undefined ? {} : { dockerRegistry: flags.dockerRegistry })), (flags.presets === undefined ? {} : { presets: flags.presets })), (flags.polyfills === undefined ? {} : { polyfills: flags.polyfills })), (flags.reactCompiler === undefined ? {} : { reactCompiler: flags.reactCompiler })), (flags.install === undefined ? {} : { install: flags.install })), { name: (((_b = flags.name) === null || _b === void 0 ? void 0 : _b.trim()) || defaultName).trim() });
49
+ : { clientServerPort: flags.clientServerPort })), (flags.serverPort === undefined ? {} : { serverPort: flags.serverPort })), (flags.dockerRegistry === undefined ? {} : { dockerRegistry: flags.dockerRegistry })), (flags.presets === undefined ? {} : { presets: flags.presets })), (flags.polyfills === undefined ? {} : { polyfills: flags.polyfills })), (flags.reactCompiler === undefined ? {} : { reactCompiler: flags.reactCompiler })), (flags.useLint === undefined ? {} : { useLint: flags.useLint })), (flags.install === undefined ? {} : { install: flags.install })), { name: (((_b = flags.name) === null || _b === void 0 ? void 0 : _b.trim()) || defaultName).trim() });
48
50
  }
@@ -1,3 +1,5 @@
1
1
  export type PackageManager = 'yarn' | 'npm';
2
2
  export declare function detectPackageManager(): PackageManager;
3
3
  export declare function installDependencies(targetDir: string, packageManager?: PackageManager): Promise<void>;
4
+ export declare function hasGitRepository(targetDir: string): boolean;
5
+ export declare function installLefthook(targetDir: string): Promise<void>;
@@ -5,17 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.detectPackageManager = detectPackageManager;
7
7
  exports.installDependencies = installDependencies;
8
+ exports.hasGitRepository = hasGitRepository;
9
+ exports.installLefthook = installLefthook;
8
10
  const child_process_1 = require("child_process");
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
9
13
  const shelljs_1 = __importDefault(require("shelljs"));
10
14
  function detectPackageManager() {
11
15
  return shelljs_1.default.which('yarn') ? 'yarn' : 'npm';
12
16
  }
13
- function installDependencies(targetDir, packageManager = detectPackageManager()) {
14
- const args = packageManager === 'yarn' ? [] : ['install'];
17
+ function runCommand(command, args, cwd) {
15
18
  return new Promise((resolve, reject) => {
16
19
  var _a, _b;
17
- const child = (0, child_process_1.spawn)(packageManager, args, {
18
- cwd: targetDir,
20
+ const child = (0, child_process_1.spawn)(command, args, {
21
+ cwd,
19
22
  stdio: ['ignore', 'pipe', 'pipe'],
20
23
  // для windows
21
24
  shell: process.platform === 'win32',
@@ -32,8 +35,18 @@ function installDependencies(targetDir, packageManager = detectPackageManager())
32
35
  resolve();
33
36
  }
34
37
  else {
35
- reject(new Error(`${packageManager} ${args.join(' ')} завершился с кодом ${code}\n${output.trim()}`));
38
+ reject(new Error(`${command} ${args.join(' ')} завершился с кодом ${code}\n${output.trim()}`));
36
39
  }
37
40
  });
38
41
  });
39
42
  }
43
+ function installDependencies(targetDir, packageManager = detectPackageManager()) {
44
+ const args = packageManager === 'yarn' ? [] : ['install'];
45
+ return runCommand(packageManager, args, targetDir);
46
+ }
47
+ function hasGitRepository(targetDir) {
48
+ return fs_1.default.existsSync(path_1.default.join(targetDir, '.git'));
49
+ }
50
+ function installLefthook(targetDir) {
51
+ return runCommand('npx', ['--no-install', 'lefthook', 'install'], targetDir);
52
+ }
@@ -114,6 +114,14 @@ function getQuestions(defaultName, prefill = {}) {
114
114
  active: 'да',
115
115
  inactive: 'нет',
116
116
  },
117
+ {
118
+ type: unlessAnswered('useLint', 'toggle'),
119
+ name: 'useLint',
120
+ message: 'Подключить arui-presets-lint',
121
+ initial: false,
122
+ active: 'да',
123
+ inactive: 'нет',
124
+ },
117
125
  {
118
126
  type: unlessAnswered('install', 'toggle'),
119
127
  name: 'install',
package/build/run.js CHANGED
@@ -67,10 +67,32 @@ function runInit() {
67
67
  spinner.fail(chalk_1.default.red('Не удалось установить зависимости'));
68
68
  throw error;
69
69
  }
70
+ if (initAnswers.useLint) {
71
+ yield tryInstallLefthook(targetDir);
72
+ }
70
73
  }
71
74
  printNextSteps(targetDir, baseCwd, initAnswers, packageManager);
72
75
  });
73
76
  }
77
+ function tryInstallLefthook(targetDir) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ if (!(0, install_dependencies_1.hasGitRepository)(targetDir)) {
80
+ console.log(` ${chalk_1.default.dim('lefthook: пропущено (нет .git). После git init выполните: npx --no-install lefthook install')}`);
81
+ return;
82
+ }
83
+ const spinner = (0, ora_1.default)({ text: 'Устанавливаю git-хуки lefthook…', color: 'cyan' }).start();
84
+ try {
85
+ yield (0, install_dependencies_1.installLefthook)(targetDir);
86
+ spinner.succeed(chalk_1.default.green('Git-хуки lefthook установлены'));
87
+ }
88
+ catch (error) {
89
+ spinner.fail(chalk_1.default.yellow('Не удалось установить lefthook'));
90
+ if (error instanceof Error && error.message) {
91
+ console.log(` ${chalk_1.default.dim(error.message)}`);
92
+ }
93
+ }
94
+ });
95
+ }
74
96
  function resolveAnswers(defaultName, flags) {
75
97
  return __awaiter(this, void 0, void 0, function* () {
76
98
  const base = (0, defaults_1.answersFromFlags)(defaultName, flags);
@@ -136,6 +158,9 @@ function mergePromptAnswers(base, answers) {
136
158
  if (answers.reactCompiler !== undefined) {
137
159
  merged.reactCompiler = Boolean(answers.reactCompiler);
138
160
  }
161
+ if (answers.useLint !== undefined) {
162
+ merged.useLint = Boolean(answers.useLint);
163
+ }
139
164
  if (answers.install !== undefined) {
140
165
  merged.install = Boolean(answers.install);
141
166
  }
@@ -153,6 +178,7 @@ function printSuccess(context, targetDir, fileCount) {
153
178
  chalk_1.default.dim(context.clientOnly ? 'clientOnly' : 'SSR'),
154
179
  chalk_1.default.dim(context.codeLoader),
155
180
  chalk_1.default.dim(context.testRunner),
181
+ ...(context.useLint ? [chalk_1.default.dim('lint')] : []),
156
182
  ].join(chalk_1.default.dim(' · '));
157
183
  console.log();
158
184
  console.log(` ${chalk_1.default.green('✔')} ${chalk_1.default.bold('Готово!')} ${chalk_1.default.dim(`проект «${context.name}» создан`)}`);
@@ -181,6 +207,12 @@ function printNextSteps(targetDir, baseCwd, answers, packageManager) {
181
207
  if (!answers.install) {
182
208
  steps.push(installCommand);
183
209
  }
210
+ if (answers.useLint && (!answers.install || !(0, install_dependencies_1.hasGitRepository)(targetDir))) {
211
+ steps.push('npx --no-install lefthook install');
212
+ }
213
+ if (answers.useLint) {
214
+ steps.push(packageManager === 'yarn' ? 'yarn lint' : 'npm run lint');
215
+ }
184
216
  steps.push(startCommand);
185
217
  console.log();
186
218
  console.log(` ${chalk_1.default.bold('Дальше')}`);
@@ -1,42 +1,44 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.appComponentTemplate = appComponentTemplate;
4
+ function tsString(value) {
5
+ return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
6
+ }
4
7
  function appComponentTemplate(ctx) {
5
8
  const styleImport = ctx.cssModules
6
9
  ? "import styles from './app.module.css';"
7
10
  : "import './app.css';";
8
11
  const rootClass = ctx.cssModules ? '{styles.root}' : "'app'";
9
12
  const titleClass = ctx.cssModules ? '{styles.title}' : "'app__title'";
13
+ const appNameLiteral = tsString(ctx.name);
10
14
  const coreImports = `import { Button } from '@alfalab/core-components/button';
11
15
  import { Gap } from '@alfalab/core-components/gap';
12
16
  import { Typography } from '@alfalab/core-components/typography';`;
13
17
  const view = (count) => ` <div className=${rootClass}>
14
18
  <Typography.Title view='medium' tag='h1' className=${titleClass}>
15
- {${JSON.stringify(ctx.name)}}
19
+ {appName}
16
20
  </Typography.Title>
17
-
18
21
  <Gap size={16} />
19
-
20
22
  <Typography.Text view='primary-medium'>Счетчик: {${count}}</Typography.Text>
21
-
22
23
  <Gap size={16} />`;
23
24
  if (ctx.useRtk) {
24
25
  return `import React from 'react';
25
26
 
26
27
  ${coreImports}
27
28
 
28
- import { useAppDispatch, useAppSelector } from '../store/hooks';
29
29
  import { decrement, increment } from '../store/counter-slice';
30
+ import { useAppDispatch, useAppSelector } from '../store/hooks';
30
31
 
31
32
  ${styleImport}
32
33
 
34
+ const appName = ${appNameLiteral};
35
+
33
36
  export function App() {
34
37
  const count = useAppSelector((state) => state.counter.value);
35
38
  const dispatch = useAppDispatch();
36
39
 
37
40
  return (
38
41
  ${view('count')}
39
-
40
42
  <Button view='accent' size={48} onClick={() => dispatch(increment())}>
41
43
  +1
42
44
  </Button>{' '}
@@ -54,12 +56,13 @@ ${coreImports}
54
56
 
55
57
  ${styleImport}
56
58
 
59
+ const appName = ${appNameLiteral};
60
+
57
61
  export function App() {
58
62
  const [count, setCount] = useState(0);
59
63
 
60
64
  return (
61
65
  ${view('count')}
62
-
63
66
  <Button view='accent' size={48} onClick={() => setCount((prev) => prev + 1)}>
64
67
  +1
65
68
  </Button>{' '}
@@ -8,24 +8,22 @@ function appStylesFileName(ctx) {
8
8
  function appStylesTemplate(ctx) {
9
9
  if (ctx.cssModules) {
10
10
  return `.root {
11
- padding: 16px;
12
- font-family: sans-serif;
11
+ padding: var(--gap-16);
13
12
  }
14
13
 
15
14
  .title {
16
- margin: 0 0 12px;
17
- font-size: 24px;
15
+ margin: var(--gap-0) var(--gap-0) var(--gap-12);
16
+ @mixin headline_small;
18
17
  }
19
18
  `;
20
19
  }
21
20
  return `.app {
22
- padding: 16px;
23
- font-family: sans-serif;
21
+ padding: var(--gap-16);
24
22
  }
25
23
 
26
24
  .app__title {
27
- margin: 0 0 12px;
28
- font-size: 24px;
25
+ margin: var(--gap-0) var(--gap-0) var(--gap-12);
26
+ @mixin headline_small;
29
27
  }
30
28
  `;
31
29
  }
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.aruiScriptsConfigTemplate = aruiScriptsConfigTemplate;
4
- // Безопасная вставка строки в конфиг
4
+ // Безопасная вставка строки в конфиг (одинарные кавычки как у prettier)
5
5
  function tsString(value) {
6
- return JSON.stringify(value);
6
+ return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
7
7
  }
8
8
  function aruiScriptsConfigTemplate(ctx) {
9
9
  const lines = [];
@@ -3,10 +3,9 @@ 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
  `;
@@ -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
+ }
@@ -57,6 +57,13 @@ defaultSemverRangePrefix: ""
57
57
  }
58
58
  function readmeTemplate(ctx) {
59
59
  const testCommand = ctx.testRunner === 'jest' ? 'arui-scripts test' : 'arui-scripts test:vitest';
60
+ const lintSection = ctx.useLint
61
+ ? `
62
+ yarn lint - eslint, stylelint, prettier, knip, secretlint
63
+ yarn lint:fix - автофикс eslint/stylelint + format
64
+ yarn format - prettier
65
+ `
66
+ : '';
60
67
  return `# ${ctx.name}
61
68
 
62
69
  Проект создан с помощью arui-scripts.
@@ -65,8 +72,7 @@ function readmeTemplate(ctx) {
65
72
 
66
73
  arui-scripts start - запуск dev-сервера
67
74
  arui-scripts build - production-сборка
68
- ${testCommand}${' '.repeat(Math.max(1, 20 - testCommand.length))}- запуск тестов
69
-
75
+ ${testCommand}${' '.repeat(Math.max(1, 20 - testCommand.length))}- запуск тестов${lintSection}
70
76
  ## Установка
71
77
 
72
78
  yarn install
@@ -18,6 +18,19 @@ 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
+ }
21
34
  const pkg = {
22
35
  name: ctx.name,
23
36
  version: '0.1.0',
@@ -27,6 +40,11 @@ function packageJsonTemplate(ctx) {
27
40
  node: '>=24.11.1',
28
41
  },
29
42
  };
43
+ if (ctx.useLint) {
44
+ pkg.prettier = 'arui-presets-lint/prettier';
45
+ pkg.stylelint = { extends: 'arui-presets-lint/stylelint' };
46
+ pkg.commitlint = { extends: './node_modules/arui-presets-lint/commitlint' };
47
+ }
30
48
  if (ctx.testRunner === 'jest') {
31
49
  pkg.jest = { preset: 'arui-scripts' };
32
50
  }
@@ -6,38 +6,17 @@ function serverEntryTemplate(ctx) {
6
6
  const storeImport = ctx.useRtk ? "\nimport { makeStore } from '../client/store';" : '';
7
7
  const renderPageFn = ctx.useRtk
8
8
  ? `function renderPage(appHtml: string, assets: Assets, preloadedState: string): string {
9
- const css = assets.css
10
- .map((href) => '<link rel="stylesheet" href="/' + href + '" />')
11
- .join('');
12
- const js = assets.js.map((src) => '<script src="/' + src + '"></script>').join('');
13
- const state = '<script>window.__PRELOADED_STATE__ = ' + preloadedState + ';</script>';
9
+ const css = assets.css.map((href) => \`<link rel="stylesheet" href="/\${href}" />\`).join('');
10
+ const js = assets.js.map((src) => \`<script src="/\${src}"></script>\`).join('');
11
+ const state = \`<script>window.__PRELOADED_STATE__ = \${preloadedState};</script>\`;
14
12
 
15
- return (
16
- '<!DOCTYPE html><html lang="ru"><head><meta charset="utf-8" /><base href="/" />' +
17
- css +
18
- '</head><body><div id="react-app">' +
19
- appHtml +
20
- '</div>' +
21
- state +
22
- js +
23
- '</body></html>'
24
- );
13
+ return \`<!DOCTYPE html><html lang="ru"><head><meta charset="utf-8" /><base href="/" />\${css}</head><body><div id="react-app">\${appHtml}</div>\${state}\${js}</body></html>\`;
25
14
  }`
26
15
  : `function renderPage(appHtml: string, assets: Assets): string {
27
- const css = assets.css
28
- .map((href) => '<link rel="stylesheet" href="/' + href + '" />')
29
- .join('');
30
- const js = assets.js.map((src) => '<script src="/' + src + '"></script>').join('');
16
+ const css = assets.css.map((href) => \`<link rel="stylesheet" href="/\${href}" />\`).join('');
17
+ const js = assets.js.map((src) => \`<script src="/\${src}"></script>\`).join('');
31
18
 
32
- return (
33
- '<!DOCTYPE html><html lang="ru"><head><meta charset="utf-8" /><base href="/" />' +
34
- css +
35
- '</head><body><div id="react-app">' +
36
- appHtml +
37
- '</div>' +
38
- js +
39
- '</body></html>'
40
- );
19
+ return \`<!DOCTYPE html><html lang="ru"><head><meta charset="utf-8" /><base href="/" />\${css}</head><body><div id="react-app">\${appHtml}</div>\${js}</body></html>\`;
41
20
  }`;
42
21
  const handlerBody = ctx.useRtk
43
22
  ? ` const assets = await readAssetsManifest();
@@ -55,12 +34,11 @@ function serverEntryTemplate(ctx) {
55
34
  const appHtml = renderToString(<App />);
56
35
 
57
36
  return renderPage(appHtml, assets);`;
58
- return `import path from 'path';
59
-
37
+ return `import React from 'react';
38
+ import { renderToString } from 'react-dom/server';${reduxImport}
60
39
  import Hapi from '@hapi/hapi';
61
40
  import Inert from '@hapi/inert';
62
- import React from 'react';
63
- import { renderToString } from 'react-dom/server';${reduxImport}
41
+ import path from 'node:path';
64
42
 
65
43
  import { readAssetsManifest } from '@alfalab/scripts-server';
66
44
 
@@ -106,10 +84,10 @@ ${handlerBody}
106
84
  });
107
85
 
108
86
  await server.start();
109
- // eslint-disable-next-line no-console
110
- console.log('Server is listening on ' + server.info.uri);
87
+ // eslint-disable-next-line no-console -- стартовый лог dev-сервера
88
+ console.log(\`Server is listening on \${server.info.uri}\`);
111
89
  }
112
90
 
113
- start();
91
+ void start();
114
92
  `;
115
93
  }
@@ -57,7 +57,7 @@ const counterSlice = createSlice({
57
57
  },
58
58
  });
59
59
 
60
- export const { increment, decrement } = counterSlice.actions;
60
+ export const { decrement, increment } = counterSlice.actions;
61
61
  export const counterReducer = counterSlice.reducer;
62
62
  `;
63
63
  }
@@ -2,13 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tsconfigTemplate = tsconfigTemplate;
4
4
  function tsconfigTemplate(ctx) {
5
- const types = ctx.testRunner === 'jest' ? ['jest', 'node', 'webpack-env'] : ['node', 'webpack-env'];
6
- const tsconfig = {
7
- extends: 'arui-scripts/tsconfig.json',
8
- include: ['global-definitions.d.ts', 'src/**/*.ts', 'src/**/*.tsx'],
9
- compilerOptions: {
10
- types,
11
- },
12
- };
13
- return `${JSON.stringify(tsconfig, null, 4)}\n`;
5
+ const types = ctx.testRunner === 'jest'
6
+ ? '["jest", "node", "webpack-env"]'
7
+ : '["node", "webpack-env"]';
8
+ return `{
9
+ "extends": "arui-scripts/tsconfig.json",
10
+ "include": ["global-definitions.d.ts", "src/**/*.ts", "src/**/*.tsx"],
11
+ "compilerOptions": {
12
+ "types": ${types}
13
+ }
14
+ }
15
+ `;
14
16
  }
package/build/types.d.ts CHANGED
@@ -13,6 +13,7 @@ export type InitAnswers = {
13
13
  presets: string;
14
14
  polyfills: boolean;
15
15
  reactCompiler: boolean;
16
+ useLint: boolean;
16
17
  install: boolean;
17
18
  };
18
19
  export type TemplateContext = {
@@ -28,6 +29,7 @@ export type TemplateContext = {
28
29
  presets: string;
29
30
  polyfills: boolean;
30
31
  reactCompiler: boolean;
32
+ useLint: boolean;
31
33
  aruiScriptsVersion: string;
32
34
  dependencies: Record<string, string>;
33
35
  devDependencies: Record<string, string>;
@@ -1,4 +1,4 @@
1
1
  /** Версия arui-scripts, которую scaffold кладёт в package.json нового проекта.
2
2
  * Генерируется скриптом scripts/sync-arui-scripts-version.js при сборке.
3
3
  */
4
- export declare const DEFAULT_ARUI_SCRIPTS_VERSION = "23.2.0";
4
+ export declare const DEFAULT_ARUI_SCRIPTS_VERSION = "23.3.1";
package/build/versions.js CHANGED
@@ -4,4 +4,4 @@ exports.DEFAULT_ARUI_SCRIPTS_VERSION = void 0;
4
4
  /** Версия arui-scripts, которую scaffold кладёт в package.json нового проекта.
5
5
  * Генерируется скриптом scripts/sync-arui-scripts-version.js при сборке.
6
6
  */
7
- exports.DEFAULT_ARUI_SCRIPTS_VERSION = '23.2.0';
7
+ exports.DEFAULT_ARUI_SCRIPTS_VERSION = '23.3.1';
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
1
  {
2
- "name": "create-arui-scripts-app",
3
- "version": "1.0.0",
4
- "description": "CLI для генерации нового react приложения на arui-scripts",
5
- "license": "MPL-2.0",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/core-ds/arui-scripts.git"
9
- },
10
- "bugs": {
11
- "url": "https://github.com/core-ds/arui-scripts/issues"
12
- },
13
- "homepage": "https://github.com/core-ds/arui-scripts/tree/master/packages/create-arui-scripts-app#readme",
14
- "bin": "./bin/create-arui-scripts-app.js",
15
- "main": "./build/run.js",
16
- "typings": "./build/run.d.ts",
17
- "files": [
18
- "assets",
19
- "bin",
20
- "build"
21
- ],
22
- "scripts": {
23
- "sync:version": "node ./scripts/sync-arui-scripts-version.js",
24
- "build": "yarn sync:version && tsc --project tsconfig.json",
25
- "test": "jest",
26
- "lint:scripts": "arui-presets-lint scripts",
27
- "format": "arui-presets-lint format",
28
- "format:check": "arui-presets-lint format:check",
29
- "lint": "yarn lint:scripts && yarn format:check",
30
- "lint:fix": "yarn lint:scripts --fix && yarn format",
31
- "audit": "yarn npm audit --severity high --environment production"
32
- },
33
- "dependencies": {
34
- "chalk": "^2.4.2",
35
- "commander": "^12",
36
- "fs-extra": "6.0.1",
37
- "ora": "^5",
38
- "prompts": "^2.4.2",
39
- "shelljs": "0.8.5",
40
- "validate-npm-package-name": "^5.0.1"
41
- },
42
- "devDependencies": {
43
- "@types/fs-extra": "^9.0.13",
44
- "@types/jest": "^29.5.14",
45
- "@types/node": "^20.19.0",
46
- "@types/prompts": "^2.4.9",
47
- "@types/shelljs": "^0.8.12",
48
- "@types/validate-npm-package-name": "^3.0.3",
49
- "jest": "28.1.3",
50
- "ts-jest": "28.0.8",
51
- "typescript": "6.0.2"
52
- },
53
- "engines": {
54
- "node": "^20.19.0 || >=22.12.0"
55
- }
56
- }
2
+ "name": "create-arui-scripts-app",
3
+ "version": "1.1.0",
4
+ "description": "CLI для генерации нового react приложения на arui-scripts",
5
+ "license": "MPL-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/core-ds/arui-scripts.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/core-ds/arui-scripts/issues"
12
+ },
13
+ "homepage": "https://github.com/core-ds/arui-scripts/tree/master/packages/create-arui-scripts-app#readme",
14
+ "bin": "./bin/create-arui-scripts-app.js",
15
+ "main": "./build/run.js",
16
+ "typings": "./build/run.d.ts",
17
+ "files": [
18
+ "assets",
19
+ "bin",
20
+ "build"
21
+ ],
22
+ "scripts": {
23
+ "sync:version": "node ./scripts/sync-arui-scripts-version.js",
24
+ "build": "yarn sync:version && tsc --project tsconfig.json",
25
+ "test": "jest",
26
+ "lint:scripts": "arui-presets-lint scripts",
27
+ "format": "arui-presets-lint format",
28
+ "format:check": "arui-presets-lint format:check",
29
+ "lint": "yarn lint:scripts && yarn format:check",
30
+ "lint:fix": "yarn lint:scripts --fix && yarn format",
31
+ "audit": "yarn npm audit --severity high --environment production"
32
+ },
33
+ "dependencies": {
34
+ "chalk": "^2.4.2",
35
+ "commander": "^12",
36
+ "fs-extra": "6.0.1",
37
+ "ora": "^5",
38
+ "prompts": "^2.4.2",
39
+ "shelljs": "0.8.5",
40
+ "validate-npm-package-name": "^5.0.1"
41
+ },
42
+ "devDependencies": {
43
+ "@types/fs-extra": "^9.0.13",
44
+ "@types/jest": "^29.5.14",
45
+ "@types/node": "^20.19.0",
46
+ "@types/prompts": "^2.4.9",
47
+ "@types/shelljs": "^0.8.12",
48
+ "@types/validate-npm-package-name": "^3.0.3",
49
+ "jest": "28.1.3",
50
+ "ts-jest": "28.0.8",
51
+ "typescript": "6.0.2"
52
+ },
53
+ "engines": {
54
+ "node": "^20.19.0 || >=22.12.0"
55
+ }
56
+ }