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 +21 -21
- package/package.json +1 -1
- package/src/core/create-project.js +11 -2
- package/src/core/parse-cli-args.js +5 -5
- package/src/core/prepare-target-directory.js +5 -5
- package/src/core/render-project-readme.js +76 -0
- package/src/features/eslint/files/eslint.config.mjs +25 -19
- package/src/features/eslint/index.js +2 -2
- package/src/features/react-router/files/src/app/layouts/app/index.tsx +1 -1
- package/src/features/react-router/files/src/app/providers/router/config/router.tsx +1 -1
- package/src/templates/base/src/index.tsx +4 -4
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
|
|
21
|
-
|
|
22
|
-
| [React](https://react.dev/) |
|
|
23
|
-
| [TypeScript](https://www.typescriptlang.org/) | Static typing
|
|
24
|
-
| [Webpack](https://webpack.js.org/) | Bundling and build pipeline
|
|
25
|
-
| [SCSS Modules](https://github.com/css-modules/css-modules) | Scoped styling
|
|
26
|
-
| [SVGR](https://react-svgr.com/) | Import SVGs as React components
|
|
27
|
-
| [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) | Bundle size inspection
|
|
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
|
|
30
|
-
| [Autoprefixer](https://github.com/postcss/autoprefixer) | Automatic CSS vendor prefixes
|
|
31
|
-
| [React Router](https://reactrouter.com/) | Client-side routing
|
|
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 --
|
|
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
|
-
| `--
|
|
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,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
|
-
|
|
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
|
-
' --
|
|
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 --
|
|
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
|
-
|
|
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 === '--
|
|
78
|
-
result.
|
|
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
|
-
|
|
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 (
|
|
41
|
-
await rm(targetDirPath, { recursive: 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 --
|
|
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,
|
|
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
|
-
//
|
|
18
|
+
// TypeScript rules
|
|
16
19
|
...tseslint.configs.recommended,
|
|
17
|
-
...tseslint.configs.stylistic,
|
|
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
|
-
//
|
|
27
|
-
stylistic.configs
|
|
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
|
-
//
|
|
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
|
|
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', //
|
|
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
|
|
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');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
-
<
|
|
13
|
+
<StrictMode>
|
|
14
14
|
<App />
|
|
15
|
-
</
|
|
16
|
-
);
|
|
15
|
+
</StrictMode>,
|
|
16
|
+
);
|