create-alistt69-kit 0.1.0 → 0.1.1
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 +20 -20
- package/package.json +1 -1
- package/src/core/create-project.js +10 -1
- 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 --force
|
|
93
|
+
npm create alistt69-kit@latest my-app --yes --force
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
## ⚙️ CLI options
|
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 {
|
|
@@ -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);
|
|
@@ -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
|
+
);
|