create-alistt69-kit 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -17,18 +17,18 @@ It generates a ready-to-run **React + TypeScript + Webpack** starter with a prac
17
17
 
18
18
  ## 🔧 What’s inside
19
19
 
20
- | Tool | Purpose | Included |
21
- |------|---------|----------|
22
- | [React](https://react.dev/) | UI library | Default |
23
- | [TypeScript](https://www.typescriptlang.org/) | Static typing | Default |
24
- | [Webpack](https://webpack.js.org/) | Bundling and build pipeline | Default |
25
- | [SCSS Modules](https://github.com/css-modules/css-modules) | Scoped styling | Default |
26
- | [SVGR](https://react-svgr.com/) | Import SVGs as React components | Default |
27
- | [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) | Bundle size inspection | Default |
20
+ | Tool | Purpose | Included |
21
+ |------|----------------------------------|----------|
22
+ | [React](https://react.dev/) | Web framework | Default |
23
+ | [TypeScript](https://www.typescriptlang.org/) | Static typing | Default |
24
+ | [Webpack](https://webpack.js.org/) | Bundling and build pipeline | Default |
25
+ | [SCSS Modules](https://github.com/css-modules/css-modules) | Scoped styling | Default |
26
+ | [SVGR](https://react-svgr.com/) | Import SVGs as React components | Default |
27
+ | [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) | Bundle size inspection | Default |
28
28
  | [ESLint](https://eslint.org/) + [eslint-stylistic](https://eslint.style/) | Code quality and stylistic rules | Optional |
29
- | [Stylelint](https://stylelint.io/) | Stylesheet linting | Optional |
30
- | [Autoprefixer](https://github.com/postcss/autoprefixer) | Automatic CSS vendor prefixes | Optional |
31
- | [React Router](https://reactrouter.com/) | Client-side routing | Optional |
29
+ | [Stylelint](https://stylelint.io/) | Stylesheet linting | Optional |
30
+ | [Autoprefixer](https://github.com/postcss/autoprefixer) | Automatic CSS vendor prefixes | Optional |
31
+ | [React Router](https://reactrouter.com/) | Client-side routing | Optional |
32
32
 
33
33
  ---
34
34
 
@@ -59,38 +59,38 @@ This starter removes that boilerplate so you can get straight to building.
59
59
  Create a new app interactively:
60
60
 
61
61
  ```bash
62
- npm create alistt69-kit
62
+ npm create alistt69-kit@latest
63
63
  ```
64
64
 
65
65
  Follow the prompts — or skip them entirely:
66
66
 
67
67
  ```bash
68
- npm create alistt69-kit my-app --yes
68
+ npm create alistt69-kit@latest my-app --yes
69
69
  ```
70
70
 
71
71
  ## 🛠️ Usage examples
72
72
 
73
73
  ```bash
74
74
  # Interactive setup
75
- npm create alistt69-kit my-app
75
+ npm create alistt69-kit@latest my-app
76
76
 
77
77
  # All defaults, no prompts
78
- npm create alistt69-kit my-app --yes
78
+ npm create alistt69-kit@latest my-app --yes
79
79
 
80
80
  # Skip dependency installation
81
- npm create alistt69-kit my-app --no-install
81
+ npm create alistt69-kit@latest my-app --no-install
82
82
 
83
83
  # Enable only selected features
84
- npm create alistt69-kit my-app --features=eslint,react-router
84
+ npm create alistt69-kit@latest my-app --features=eslint,react-router
85
85
 
86
86
  # Enable all optional features
87
- npm create alistt69-kit my-app --features=all
87
+ npm create alistt69-kit@latest my-app --features=all
88
88
 
89
89
  # Use pnpm as package manager
90
- npm create alistt69-kit my-app --pm pnpm
90
+ npm create alistt69-kit@latest my-app --pm pnpm
91
91
 
92
92
  # Overwrite existing directory
93
- npm create alistt69-kit my-app --yes --force
93
+ npm create alistt69-kit@latest my-app --yes --overwrite
94
94
  ```
95
95
 
96
96
  ## ⚙️ CLI options
@@ -98,7 +98,7 @@ npm create alistt69-kit my-app --yes --force
98
98
  | Option | Alias | Description |
99
99
  |--------|-------|-------------|
100
100
  | `--yes` | `-y` | Skip prompts, use defaults |
101
- | `--force` | — | Overwrite target directory if it exists |
101
+ | `--overwrite` | — | Overwrite target directory if it exists |
102
102
  | `--no-install` | — | Skip dependency installation |
103
103
  | `--features <list>` | — | Enable specific features (`eslint`, `react-router`, `all`) |
104
104
  | `--pm <name>` | — | Choose package manager (`npm`, `pnpm`, `yarn`) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-alistt69-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Opinionated React + TypeScript + Webpack project generator by alistt69",
5
5
  "keywords": [
6
6
  "create",
@@ -1,5 +1,4 @@
1
1
  import { outro, spinner } from '@clack/prompts';
2
- import process from 'node:process';
3
2
  import { format } from '../utils/console-format.js';
4
3
  import { getRunScriptCommand } from '../utils/package-manager.js';
5
4
  import { applyFeatures } from './apply-features.js';
@@ -9,6 +8,7 @@ import { installDependencies } from './install-dependencies.js';
9
8
  import { prepareTargetDirectory } from './prepare-target-directory.js';
10
9
  import { replaceTokens } from './replace-tokens.js';
11
10
  import { restoreSpecialFiles } from './restore-special-files.js';
11
+ import { renderProjectReadme } from './render-project-readme.js';
12
12
 
13
13
  export async function createProject(cliArgs = {}) {
14
14
  const {
@@ -20,7 +20,7 @@ export async function createProject(cliArgs = {}) {
20
20
 
21
21
  const { targetDirPath } = await prepareTargetDirectory({
22
22
  projectName,
23
- force: cliArgs.force,
23
+ overwrite: cliArgs.overwrite,
24
24
  yes: cliArgs.yes,
25
25
  });
26
26
 
@@ -50,6 +50,15 @@ export async function createProject(cliArgs = {}) {
50
50
  progress.stop('Features applied');
51
51
  }
52
52
 
53
+ progress.start('Generating README...');
54
+ await renderProjectReadme({
55
+ projectPath: targetDirPath,
56
+ projectName,
57
+ selectedFeatureIds,
58
+ packageManager,
59
+ });
60
+ progress.stop('README generated');
61
+
53
62
  if (shouldInstallDependencies) {
54
63
  progress.start(`Installing dependencies with ${packageManager}...`);
55
64
  await installDependencies(targetDirPath, packageManager);
@@ -29,7 +29,7 @@ export function formatHelpMessage() {
29
29
  '',
30
30
  'Options:',
31
31
  ' -y, --yes Skip prompts and use defaults',
32
- ' --force Overwrite target directory if it exists',
32
+ ' --overwrite Overwrite target directory if it exists',
33
33
  ' --no-install Do not install dependencies',
34
34
  ' --features <comma-list> Example: eslint,stylelint,react-router',
35
35
  ' --features all Enable all features',
@@ -46,7 +46,7 @@ export function formatHelpMessage() {
46
46
  ' create-alistt69-kit my-app --features=all',
47
47
  ' create-alistt69-kit my-app --pm pnpm --no-install',
48
48
  ' create-alistt69-kit my-app --yes',
49
- ' create-alistt69-kit my-app --yes --force',
49
+ ' create-alistt69-kit my-app --yes --overwrite',
50
50
  ].join('\n');
51
51
  }
52
52
 
@@ -57,7 +57,7 @@ export function parseCliArgs(argv) {
57
57
  shouldInstallDependencies: undefined,
58
58
  packageManager: undefined,
59
59
  yes: false,
60
- force: false,
60
+ overwrite: false,
61
61
  showHelp: false,
62
62
  };
63
63
 
@@ -74,8 +74,8 @@ export function parseCliArgs(argv) {
74
74
  continue;
75
75
  }
76
76
 
77
- if (arg === '--force') {
78
- result.force = true;
77
+ if (arg === '--overwrite') {
78
+ result.overwrite = true;
79
79
  continue;
80
80
  }
81
81
 
@@ -14,7 +14,7 @@ async function pathExists(targetDirPath) {
14
14
 
15
15
  export async function prepareTargetDirectory({
16
16
  projectName,
17
- force = false,
17
+ overwrite = false,
18
18
  yes = false,
19
19
  }) {
20
20
  const targetDirPath = resolve(process.cwd(), projectName);
@@ -37,8 +37,8 @@ export async function prepareTargetDirectory({
37
37
  };
38
38
  }
39
39
 
40
- if (force) {
41
- await rm(targetDirPath, { recursive: true, force: true });
40
+ if (overwrite) {
41
+ await rm(targetDirPath, { recursive: true, overwrite: true });
42
42
 
43
43
  return {
44
44
  targetDirPath,
@@ -48,7 +48,7 @@ export async function prepareTargetDirectory({
48
48
 
49
49
  if (yes) {
50
50
  throw new Error(
51
- `Directory already exists and is not empty: ${targetDirPath}. Use --force to overwrite it.`,
51
+ `Directory already exists and is not empty: ${targetDirPath}. Use --overwrite to overwrite it.`,
52
52
  );
53
53
  }
54
54
 
@@ -61,7 +61,7 @@ export async function prepareTargetDirectory({
61
61
  process.exit(0);
62
62
  }
63
63
 
64
- await rm(targetDirPath, { recursive: true, force: true });
64
+ await rm(targetDirPath, { recursive: true, overwrite: true });
65
65
 
66
66
  return {
67
67
  targetDirPath,
@@ -0,0 +1,76 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { featuresById } from '../features/index.js';
4
+ import { readPackageJson } from '../utils/package-json.js';
5
+ import { getRunScriptCommand } from '../utils/package-manager.js';
6
+
7
+ const scriptDescriptions = {
8
+ dev: 'Start development server',
9
+ start: 'Start development server',
10
+ build: 'Build for production',
11
+ 'build:dev': 'Build in development mode',
12
+ 'build:prod': 'Build in production mode',
13
+ typecheck: 'Run TypeScript type check',
14
+ lint: 'Run ESLint',
15
+ 'lint:fix': 'Run ESLint with autofix',
16
+ 'lint:styles': 'Run Stylelint',
17
+ 'lint:styles:fix': 'Run Stylelint with autofix',
18
+ };
19
+
20
+ function formatFeatureList(selectedFeatureIds) {
21
+ if (selectedFeatureIds.length === 0) {
22
+ return '- None';
23
+ }
24
+
25
+ return selectedFeatureIds
26
+ .map((featureId) => {
27
+ const feature = featuresById.get(featureId);
28
+
29
+ return `- ${feature?.title ?? featureId}`;
30
+ })
31
+ .join('\n');
32
+ }
33
+
34
+ function formatScripts(packageManager, scripts) {
35
+ const entries = Object.entries(scripts);
36
+
37
+ if (entries.length === 0) {
38
+ return '_No scripts available._';
39
+ }
40
+
41
+ return entries.map(([scriptName]) => {
42
+ const command = getRunScriptCommand(packageManager, scriptName);
43
+ const description = scriptDescriptions[scriptName] ?? 'Project script';
44
+
45
+ return `- \`${command}\` — ${description}`;
46
+ }).join('\n');
47
+ }
48
+
49
+ export async function renderProjectReadme({
50
+ projectPath,
51
+ projectName,
52
+ selectedFeatureIds,
53
+ packageManager,
54
+ }) {
55
+ const packageJson = await readPackageJson(projectPath);
56
+
57
+ const readmeContent = [
58
+ `# ${projectName}`,
59
+ '',
60
+ 'Created with `create-alistt69-kit`.',
61
+ '',
62
+ '## Enabled features',
63
+ '',
64
+ formatFeatureList(selectedFeatureIds),
65
+ '',
66
+ '## Available scripts',
67
+ '',
68
+ formatScripts(packageManager, packageJson.scripts ?? {}),
69
+ ].join('\n');
70
+
71
+ await writeFile(
72
+ resolve(projectPath, 'README.md'),
73
+ `${readmeContent}\n`,
74
+ 'utf8',
75
+ );
76
+ }
@@ -7,14 +7,19 @@ import unused from 'eslint-plugin-unused-imports';
7
7
  import tseslint from 'typescript-eslint';
8
8
 
9
9
  export default [
10
+ // ESLint recommended rules
10
11
  js.configs.recommended,
12
+
13
+ // Paths to ignore
11
14
  {
12
- ignores: ['**/dist', '**/node_modules', '**/build', 'webpack.config.ts'],
15
+ ignores: ['**/dist', '**/node_modules', '**/build', 'webpack.config.ts', 'postcss.config.cjs'],
13
16
  },
14
17
 
15
- // TS base + parser
18
+ // TypeScript rules
16
19
  ...tseslint.configs.recommended,
17
- ...tseslint.configs.stylistic, // TS-стилистика
20
+ ...tseslint.configs.stylistic,
21
+
22
+ // TypeScript parser configuration
18
23
  {
19
24
  files: ['**/*.ts', '**/*.tsx'],
20
25
  languageOptions: {
@@ -23,10 +28,10 @@ export default [
23
28
  },
24
29
  },
25
30
 
26
- // Глобальные стилевые правила (замена prettier)
27
- stylistic.configs['recommended'],
31
+ // Stylistic rules (Prettier alternative)
32
+ stylistic.configs.recommended,
28
33
 
29
- // Реакт / импорты / утиль
34
+ // Main rule configuration
30
35
  {
31
36
  plugins: {
32
37
  react,
@@ -37,7 +42,7 @@ export default [
37
42
  },
38
43
  settings: { react: { version: 'detect' } },
39
44
  rules: {
40
- // ==== БАЗОВОЕ ФОРМАТИРОВАНИЕ (как prettier) ====
45
+ // === Formatting ===
41
46
  '@stylistic/indent': ['error', 4, { SwitchCase: 1 }],
42
47
  '@stylistic/semi': ['error', 'always'],
43
48
  '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
@@ -51,16 +56,16 @@ export default [
51
56
  multiline: { delimiter: 'semi', requireLast: true },
52
57
  singleline: { delimiter: 'semi', requireLast: false },
53
58
  }],
54
- // запрет однострочного return (<JSX/>)
55
- // (через строгую трактовку лишних скобок вокруг однострочного выражения)
56
59
  '@stylistic/no-extra-parens': 'off',
57
60
  '@stylistic/jsx-indent-props': 'off',
58
61
  '@stylistic/multiline-ternary': 'off',
59
62
 
60
- // Пустые строки/пробелы
63
+ // === Whitespace ===
61
64
  'no-trailing-spaces': ['warn', { skipBlankLines: false }],
62
65
  'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 1 }],
63
66
  'comma-style': ['error', 'last'],
67
+
68
+ // === Line length ===
64
69
  'max-len': ['error', {
65
70
  code: 120,
66
71
  tabWidth: 4,
@@ -70,7 +75,7 @@ export default [
70
75
  ignoreTemplateLiterals: true,
71
76
  }],
72
77
 
73
- // Импорты
78
+ // === Imports ===
74
79
  'unused-imports/no-unused-imports': 'warn',
75
80
  'unused-imports/no-unused-vars': ['warn', {
76
81
  args: 'after-used',
@@ -94,13 +99,14 @@ export default [
94
99
  alphabetize: { order: 'asc', caseInsensitive: true },
95
100
  }],
96
101
 
97
- // React / JSX
102
+ // === React ===
98
103
  ...react.configs.recommended.rules,
99
104
  ...reactHooks.configs.recommended.rules,
100
- 'react/react-in-jsx-scope': 'off',
101
- 'react/jsx-uses-react': 'off',
102
- 'react/prop-types': 'off',
105
+ 'react/react-in-jsx-scope': 'off', // Not needed in React 17+
106
+ 'react/jsx-uses-react': 'off', // Not needed in React 17+
107
+ 'react/prop-types': 'off', // TypeScript handles this
103
108
 
109
+ // === JSX formatting ===
104
110
  'react/jsx-indent': ['error', 4],
105
111
  'react/jsx-indent-props': ['error', 4],
106
112
  'react/jsx-curly-spacing': ['error', { when: 'never', children: true }],
@@ -115,14 +121,14 @@ export default [
115
121
  'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],
116
122
  'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
117
123
 
118
- // Прочее
119
- '@typescript-eslint/no-unused-vars': 'off', // перекрываем unused-imports
124
+ // === General ===
125
+ '@typescript-eslint/no-unused-vars': 'off', // Handled by unused-imports
120
126
  'no-console': ['warn', { allow: ['error'] }],
121
127
  'no-restricted-imports': ['error', {
122
128
  name: 'react',
123
129
  importNames: ['default'],
124
- message: 'React импортировать не надо (v17+). Удалите import React from "react".',
130
+ message: 'React import is not needed in React 17+. Remove import React from "react".',
125
131
  }],
126
132
  },
127
133
  },
128
- ];
134
+ ];
@@ -22,8 +22,8 @@ export const eslintFeature = {
22
22
  });
23
23
 
24
24
  await addScripts(projectPath, {
25
- lint: 'eslint .',
26
- 'lint:fix': 'eslint . --fix',
25
+ lint: 'eslint . --ext .js,.jsx,.ts,.tsx',
26
+ 'lint:fix': 'eslint . --ext .js,.jsx,.ts,.tsx --fix',
27
27
  });
28
28
 
29
29
  const filesDirPath = resolve(currentDirPath, 'files');
@@ -14,4 +14,4 @@ export default function AppLayout() {
14
14
  </main>
15
15
  </div>
16
16
  );
17
- }
17
+ }
@@ -10,4 +10,4 @@ export const appRouter = createBrowserRouter(
10
10
  <Route path="*" element={<Error />} />
11
11
  </Route>,
12
12
  ),
13
- );
13
+ );
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
3
  import App from './app/App';
4
4
  import './styles/index.scss';
@@ -10,7 +10,7 @@ if (!container) {
10
10
  }
11
11
 
12
12
  createRoot(container).render(
13
- <React.StrictMode>
13
+ <StrictMode>
14
14
  <App />
15
- </React.StrictMode>,
16
- );
15
+ </StrictMode>,
16
+ );