@wsxjs/cli 0.0.18
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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +892 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WSXJS Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @wsxjs/cli
|
|
2
|
+
|
|
3
|
+
CLI tool for WSXJS initialization and configuration with beautiful Ink-based UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D @wsxjs/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Initialize WSXJS
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx wsx init
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This command will interactively guide you through setting up WSXJS in your project:
|
|
20
|
+
|
|
21
|
+
- ✅ Configure TypeScript (`tsconfig.json`)
|
|
22
|
+
- ✅ Configure Vite (`vite.config.ts`)
|
|
23
|
+
- ✅ Generate `wsx.d.ts` type declarations
|
|
24
|
+
- ✅ Configure ESLint with WSX rules
|
|
25
|
+
|
|
26
|
+
The CLI uses [Ink](https://github.com/vadimdemedes/ink) to provide a beautiful, interactive terminal UI with real-time progress updates.
|
|
27
|
+
|
|
28
|
+
### Check Configuration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx wsx check
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Check your current WSXJS configuration and get suggestions for improvements.
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
### `wsx init`
|
|
39
|
+
|
|
40
|
+
Initialize WSXJS in your project with an interactive setup wizard.
|
|
41
|
+
|
|
42
|
+
**Options:**
|
|
43
|
+
- `--skip-tsconfig`: Skip TypeScript configuration
|
|
44
|
+
- `--skip-vite`: Skip Vite configuration
|
|
45
|
+
- `--skip-eslint`: Skip ESLint configuration
|
|
46
|
+
- `--skip-types`: Skip `wsx.d.ts` generation
|
|
47
|
+
- `--use-tsconfig-package`: Use `@wsxjs/wsx-tsconfig` package
|
|
48
|
+
- `--use-decorators`: Enable decorator support (`@state`)
|
|
49
|
+
- `--no-interactive`: Skip interactive prompts (use command-line options only)
|
|
50
|
+
|
|
51
|
+
**Examples:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Full interactive setup
|
|
55
|
+
npx wsx init
|
|
56
|
+
|
|
57
|
+
# Skip TypeScript config
|
|
58
|
+
npx wsx init --skip-tsconfig
|
|
59
|
+
|
|
60
|
+
# Use @wsxjs/wsx-tsconfig package
|
|
61
|
+
npx wsx init --use-tsconfig-package
|
|
62
|
+
|
|
63
|
+
# Non-interactive mode
|
|
64
|
+
npx wsx init --skip-types --skip-eslint --no-interactive
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `wsx check`
|
|
68
|
+
|
|
69
|
+
Check your WSXJS configuration and display any issues or suggestions.
|
|
70
|
+
|
|
71
|
+
**Example:**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx wsx check
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Output:
|
|
78
|
+
```
|
|
79
|
+
🔍 WSX Configuration Check
|
|
80
|
+
|
|
81
|
+
Status:
|
|
82
|
+
wsx.d.ts: ✓ Found
|
|
83
|
+
tsconfig.json: ✓ Valid
|
|
84
|
+
vite.config: ✓ Valid
|
|
85
|
+
|
|
86
|
+
✅ All checks passed! Your WSX configuration looks good.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
### 🎨 Beautiful Terminal UI
|
|
92
|
+
|
|
93
|
+
The CLI uses [Ink](https://github.com/vadimdemedes/ink) to provide a React-based terminal UI with:
|
|
94
|
+
- Real-time progress indicators
|
|
95
|
+
- Spinner animations
|
|
96
|
+
- Color-coded status messages
|
|
97
|
+
- Step-by-step progress tracking
|
|
98
|
+
|
|
99
|
+
### ⚙️ Smart Configuration
|
|
100
|
+
|
|
101
|
+
- **TypeScript**: Automatically configures `tsconfig.json` with WSX-compatible settings
|
|
102
|
+
- **Vite**: Adds `@wsxjs/wsx-vite-plugin` to your Vite configuration
|
|
103
|
+
- **ESLint**: Configures ESLint with WSX-specific rules
|
|
104
|
+
- **Type Declarations**: Generates `wsx.d.ts` for `.wsx` file type support
|
|
105
|
+
|
|
106
|
+
### 🔍 Configuration Validation
|
|
107
|
+
|
|
108
|
+
The `check` command validates your setup and provides:
|
|
109
|
+
- Issue detection
|
|
110
|
+
- Actionable suggestions
|
|
111
|
+
- Configuration status overview
|
|
112
|
+
|
|
113
|
+
## Examples
|
|
114
|
+
|
|
115
|
+
### Basic Setup
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Install WSXJS packages
|
|
119
|
+
npm install @wsxjs/wsx-core @wsxjs/wsx-vite-plugin
|
|
120
|
+
|
|
121
|
+
# Install CLI tool
|
|
122
|
+
npm install -D @wsxjs/cli
|
|
123
|
+
|
|
124
|
+
# Initialize with interactive UI
|
|
125
|
+
npx wsx init
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Advanced Setup
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Use @wsxjs/wsx-tsconfig package
|
|
132
|
+
npx wsx init --use-tsconfig-package --use-decorators
|
|
133
|
+
|
|
134
|
+
# Skip certain configurations
|
|
135
|
+
npx wsx init --skip-vite --skip-eslint
|
|
136
|
+
|
|
137
|
+
# Non-interactive mode
|
|
138
|
+
npx wsx init --no-interactive --use-decorators
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Architecture
|
|
142
|
+
|
|
143
|
+
The CLI is built with:
|
|
144
|
+
- **[Commander.js](https://github.com/tj/commander.js)**: Command-line interface
|
|
145
|
+
- **[Ink](https://github.com/vadimdemedes/ink)**: React for CLI apps
|
|
146
|
+
- **[Inquirer](https://github.com/SBoudrias/Inquirer.js)**: Interactive prompts
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import { existsSync as existsSync4, mkdirSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8
|
+
import { join as join4 } from "path";
|
|
9
|
+
import React2 from "react";
|
|
10
|
+
import { render } from "ink";
|
|
11
|
+
import inquirer from "inquirer";
|
|
12
|
+
|
|
13
|
+
// src/ui/InitUI.tsx
|
|
14
|
+
import { useState, useEffect, useRef } from "react";
|
|
15
|
+
import { Box, Text, Newline } from "ink";
|
|
16
|
+
import Spinner from "ink-spinner";
|
|
17
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
+
var InitUI = ({ onComplete, configSteps }) => {
|
|
19
|
+
const [steps, setSteps] = useState(
|
|
20
|
+
configSteps.map((step) => ({
|
|
21
|
+
name: step.name,
|
|
22
|
+
status: step.skip ? "skipped" : "pending",
|
|
23
|
+
message: step.skip ? "\u5DF2\u8DF3\u8FC7" : void 0
|
|
24
|
+
}))
|
|
25
|
+
);
|
|
26
|
+
const [allComplete, setAllComplete] = useState(false);
|
|
27
|
+
const executingRef = useRef(false);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const allSkipped = steps.every((s) => s.status === "skipped");
|
|
30
|
+
if (allSkipped) {
|
|
31
|
+
setAllComplete(true);
|
|
32
|
+
setTimeout(() => onComplete(), 1e3);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (executingRef.current) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
executingRef.current = true;
|
|
39
|
+
const runSteps = async () => {
|
|
40
|
+
const stepsToExecute = configSteps.filter((step) => !step.skip);
|
|
41
|
+
for (let i = 0; i < stepsToExecute.length; i++) {
|
|
42
|
+
const step = stepsToExecute[i];
|
|
43
|
+
setSteps((prev) => {
|
|
44
|
+
const newSteps = [...prev];
|
|
45
|
+
const index = newSteps.findIndex((s) => s.name === step.name);
|
|
46
|
+
if (index !== -1) {
|
|
47
|
+
newSteps[index] = {
|
|
48
|
+
...newSteps[index],
|
|
49
|
+
status: "running"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return newSteps;
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
const result = await step.execute();
|
|
56
|
+
setSteps((prev) => {
|
|
57
|
+
const newSteps = [...prev];
|
|
58
|
+
const index = newSteps.findIndex((s) => s.name === step.name);
|
|
59
|
+
if (index !== -1) {
|
|
60
|
+
newSteps[index] = {
|
|
61
|
+
...newSteps[index],
|
|
62
|
+
status: result.success ? "completed" : "error",
|
|
63
|
+
message: result.message
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return newSteps;
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
setSteps((prev) => {
|
|
70
|
+
const newSteps = [...prev];
|
|
71
|
+
const index = newSteps.findIndex((s) => s.name === step.name);
|
|
72
|
+
if (index !== -1) {
|
|
73
|
+
newSteps[index] = {
|
|
74
|
+
...newSteps[index],
|
|
75
|
+
status: "error",
|
|
76
|
+
message: `\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return newSteps;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
83
|
+
}
|
|
84
|
+
setAllComplete(true);
|
|
85
|
+
setTimeout(() => onComplete(), 1500);
|
|
86
|
+
};
|
|
87
|
+
const timer = setTimeout(() => {
|
|
88
|
+
runSteps().catch((error) => {
|
|
89
|
+
setSteps((prev) => {
|
|
90
|
+
const newSteps = [...prev];
|
|
91
|
+
const errorStep = newSteps.find((s) => s.status === "running");
|
|
92
|
+
if (errorStep) {
|
|
93
|
+
const index = newSteps.findIndex((s) => s.name === errorStep.name);
|
|
94
|
+
if (index !== -1) {
|
|
95
|
+
newSteps[index] = {
|
|
96
|
+
...newSteps[index],
|
|
97
|
+
status: "error",
|
|
98
|
+
message: `\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return newSteps;
|
|
103
|
+
});
|
|
104
|
+
setAllComplete(true);
|
|
105
|
+
setTimeout(() => onComplete(), 1500);
|
|
106
|
+
});
|
|
107
|
+
}, 300);
|
|
108
|
+
return () => {
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
executingRef.current = false;
|
|
111
|
+
};
|
|
112
|
+
}, []);
|
|
113
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
|
|
114
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "blue", children: "\u{1F680} \u6B63\u5728\u521D\u59CB\u5316 WSXJS..." }),
|
|
115
|
+
/* @__PURE__ */ jsx(Newline, {}),
|
|
116
|
+
steps.map((step) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
117
|
+
step.status === "pending" && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
118
|
+
"\u23F3 ",
|
|
119
|
+
step.name,
|
|
120
|
+
": \u7B49\u5F85\u4E2D..."
|
|
121
|
+
] }),
|
|
122
|
+
step.status === "running" && /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
123
|
+
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
124
|
+
" ",
|
|
125
|
+
step.name,
|
|
126
|
+
": \u914D\u7F6E\u4E2D..."
|
|
127
|
+
] }),
|
|
128
|
+
step.status === "completed" && /* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
129
|
+
"\u2713 ",
|
|
130
|
+
step.name,
|
|
131
|
+
": ",
|
|
132
|
+
step.message || "\u5B8C\u6210"
|
|
133
|
+
] }),
|
|
134
|
+
step.status === "skipped" && /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
135
|
+
"\u2298 ",
|
|
136
|
+
step.name,
|
|
137
|
+
": ",
|
|
138
|
+
step.message || "\u5DF2\u8DF3\u8FC7"
|
|
139
|
+
] }),
|
|
140
|
+
step.status === "error" && /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
141
|
+
"\u2717 ",
|
|
142
|
+
step.name,
|
|
143
|
+
": ",
|
|
144
|
+
step.message || "\u5931\u8D25"
|
|
145
|
+
] })
|
|
146
|
+
] }, step.name)),
|
|
147
|
+
allComplete && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
148
|
+
/* @__PURE__ */ jsx(Newline, {}),
|
|
149
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "\u2705 WSXJS \u521D\u59CB\u5316\u5B8C\u6210\uFF01" }),
|
|
150
|
+
/* @__PURE__ */ jsx(Newline, {}),
|
|
151
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\u4E0B\u4E00\u6B65\uFF1A" }),
|
|
152
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "1. \u5B89\u88C5\u4F9D\u8D56: npm install" }),
|
|
153
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "2. \u5F00\u59CB\u5F00\u53D1: npm run dev" })
|
|
154
|
+
] })
|
|
155
|
+
] });
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/config/configure-typescript.ts
|
|
159
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
160
|
+
import { join } from "path";
|
|
161
|
+
async function configureTypeScript(projectRoot, options = {}) {
|
|
162
|
+
const tsconfigPath = join(projectRoot, "tsconfig.json");
|
|
163
|
+
const { useTsConfigPackage = false, useDecorators = true } = options;
|
|
164
|
+
if (useTsConfigPackage) {
|
|
165
|
+
const config = {
|
|
166
|
+
extends: "@wsxjs/wsx-tsconfig/tsconfig.base.json",
|
|
167
|
+
compilerOptions: {
|
|
168
|
+
outDir: "./dist"
|
|
169
|
+
},
|
|
170
|
+
include: ["src/**/*"]
|
|
171
|
+
};
|
|
172
|
+
if (existsSync(tsconfigPath)) {
|
|
173
|
+
try {
|
|
174
|
+
const existing = JSON.parse(readFileSync(tsconfigPath, "utf-8"));
|
|
175
|
+
const merged = mergeTsConfig(existing, config);
|
|
176
|
+
writeFileSync(tsconfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
message: "\u5DF2\u66F4\u65B0 tsconfig.json\uFF08\u4F7F\u7528 @wsxjs/wsx-tsconfig\uFF09",
|
|
180
|
+
created: false
|
|
181
|
+
};
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
message: `\u65E0\u6CD5\u89E3\u6790\u73B0\u6709 tsconfig.json: ${error}`,
|
|
186
|
+
created: false
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
191
|
+
return {
|
|
192
|
+
success: true,
|
|
193
|
+
message: "\u5DF2\u521B\u5EFA tsconfig.json\uFF08\u4F7F\u7528 @wsxjs/wsx-tsconfig\uFF09",
|
|
194
|
+
created: true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
const compilerOptions = {
|
|
199
|
+
jsx: "react-jsx",
|
|
200
|
+
jsxImportSource: "@wsxjs/wsx-core",
|
|
201
|
+
types: ["@wsxjs/wsx-core"]
|
|
202
|
+
};
|
|
203
|
+
if (useDecorators) {
|
|
204
|
+
compilerOptions.experimentalDecorators = true;
|
|
205
|
+
compilerOptions.useDefineForClassFields = false;
|
|
206
|
+
}
|
|
207
|
+
const config = {
|
|
208
|
+
compilerOptions,
|
|
209
|
+
include: ["src/**/*"]
|
|
210
|
+
};
|
|
211
|
+
if (existsSync(tsconfigPath)) {
|
|
212
|
+
try {
|
|
213
|
+
const existing = JSON.parse(readFileSync(tsconfigPath, "utf-8"));
|
|
214
|
+
const merged = mergeTsConfig(existing, config);
|
|
215
|
+
writeFileSync(tsconfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
message: "\u5DF2\u66F4\u65B0 tsconfig.json",
|
|
219
|
+
created: false
|
|
220
|
+
};
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
message: `\u65E0\u6CD5\u89E3\u6790\u73B0\u6709 tsconfig.json: ${error}`,
|
|
225
|
+
created: false
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
message: "\u5DF2\u521B\u5EFA tsconfig.json",
|
|
233
|
+
created: true
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function mergeTsConfig(existing, newConfig) {
|
|
239
|
+
const merged = { ...existing };
|
|
240
|
+
if (newConfig.compilerOptions) {
|
|
241
|
+
merged.compilerOptions = {
|
|
242
|
+
...existing.compilerOptions,
|
|
243
|
+
...newConfig.compilerOptions
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (newConfig.include) {
|
|
247
|
+
const existingInclude = existing.include || [];
|
|
248
|
+
const newInclude = newConfig.include || [];
|
|
249
|
+
merged.include = [.../* @__PURE__ */ new Set([...existingInclude, ...newInclude])];
|
|
250
|
+
}
|
|
251
|
+
if (existing.extends && !newConfig.extends) {
|
|
252
|
+
} else if (newConfig.extends) {
|
|
253
|
+
merged.extends = newConfig.extends;
|
|
254
|
+
}
|
|
255
|
+
return merged;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/config/configure-vite.ts
|
|
259
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
260
|
+
import { join as join2 } from "path";
|
|
261
|
+
async function configureVite(projectRoot) {
|
|
262
|
+
const viteConfigPaths = [
|
|
263
|
+
{ path: join2(projectRoot, "vite.config.ts"), ext: "ts" },
|
|
264
|
+
{ path: join2(projectRoot, "vite.config.js"), ext: "js" },
|
|
265
|
+
{ path: join2(projectRoot, "vite.config.mts"), ext: "mts" },
|
|
266
|
+
{ path: join2(projectRoot, "vite.config.mjs"), ext: "mjs" }
|
|
267
|
+
];
|
|
268
|
+
let existingConfig = null;
|
|
269
|
+
for (const configPath of viteConfigPaths) {
|
|
270
|
+
if (existsSync2(configPath.path)) {
|
|
271
|
+
existingConfig = configPath;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (existingConfig) {
|
|
276
|
+
try {
|
|
277
|
+
const content = readFileSync2(existingConfig.path, "utf-8");
|
|
278
|
+
const hasWsxImport = content.includes("@wsxjs/wsx-vite-plugin");
|
|
279
|
+
const hasWsxPlugin = content.includes("wsx()") || content.includes("wsx(");
|
|
280
|
+
if (hasWsxImport && hasWsxPlugin) {
|
|
281
|
+
return {
|
|
282
|
+
success: true,
|
|
283
|
+
message: "vite.config \u5DF2\u5305\u542B WSX \u63D2\u4EF6",
|
|
284
|
+
created: false
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const updated = addWsxPluginToViteConfig(content, existingConfig.ext);
|
|
288
|
+
writeFileSync2(existingConfig.path, updated, "utf-8");
|
|
289
|
+
return {
|
|
290
|
+
success: true,
|
|
291
|
+
message: `\u5DF2\u66F4\u65B0 ${existingConfig.path}`,
|
|
292
|
+
created: false
|
|
293
|
+
};
|
|
294
|
+
} catch (error) {
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6/\u66F4\u65B0 vite.config: ${error}`,
|
|
298
|
+
created: false
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
const configContent = `import { defineConfig } from "vite";
|
|
303
|
+
import { wsx } from "@wsxjs/wsx-vite-plugin";
|
|
304
|
+
|
|
305
|
+
export default defineConfig({
|
|
306
|
+
plugins: [wsx()],
|
|
307
|
+
});
|
|
308
|
+
`;
|
|
309
|
+
const newConfigPath = join2(projectRoot, "vite.config.ts");
|
|
310
|
+
writeFileSync2(newConfigPath, configContent, "utf-8");
|
|
311
|
+
return {
|
|
312
|
+
success: true,
|
|
313
|
+
message: "\u5DF2\u521B\u5EFA vite.config.ts",
|
|
314
|
+
created: true
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function addWsxPluginToViteConfig(content, _ext) {
|
|
319
|
+
const hasWsxImport = content.includes("@wsxjs/wsx-vite-plugin");
|
|
320
|
+
const hasDefineConfigImport = content.includes("defineConfig");
|
|
321
|
+
let updated = content;
|
|
322
|
+
if (!hasWsxImport) {
|
|
323
|
+
const importRegex = /^import\s+.*?from\s+['"].*?['"];?$/gm;
|
|
324
|
+
const imports = content.match(importRegex);
|
|
325
|
+
if (imports && imports.length > 0) {
|
|
326
|
+
const lastImport = imports[imports.length - 1];
|
|
327
|
+
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
328
|
+
const insertIndex = lastImportIndex + lastImport.length;
|
|
329
|
+
const wsxImport = hasDefineConfigImport ? `import { wsx } from "@wsxjs/wsx-vite-plugin";
|
|
330
|
+
` : `import { defineConfig } from "vite";
|
|
331
|
+
import { wsx } from "@wsxjs/wsx-vite-plugin";
|
|
332
|
+
`;
|
|
333
|
+
updated = content.slice(0, insertIndex) + "\n" + wsxImport + content.slice(insertIndex);
|
|
334
|
+
} else {
|
|
335
|
+
const wsxImport = hasDefineConfigImport ? `import { wsx } from "@wsxjs/wsx-vite-plugin";
|
|
336
|
+
|
|
337
|
+
` : `import { defineConfig } from "vite";
|
|
338
|
+
import { wsx } from "@wsxjs/wsx-vite-plugin";
|
|
339
|
+
|
|
340
|
+
`;
|
|
341
|
+
updated = wsxImport + content;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (!updated.includes("wsx()") && !updated.includes("wsx(")) {
|
|
345
|
+
const pluginsArrayRegex = /plugins:\s*\[([^\]]*)\]/s;
|
|
346
|
+
const match = updated.match(pluginsArrayRegex);
|
|
347
|
+
if (match) {
|
|
348
|
+
const pluginsContent = match[1].trim();
|
|
349
|
+
const newPluginsContent = pluginsContent ? `${pluginsContent}
|
|
350
|
+
wsx(),` : `wsx(),`;
|
|
351
|
+
updated = updated.replace(
|
|
352
|
+
pluginsArrayRegex,
|
|
353
|
+
`plugins: [
|
|
354
|
+
${newPluginsContent}
|
|
355
|
+
]`
|
|
356
|
+
);
|
|
357
|
+
} else {
|
|
358
|
+
const defineConfigRegex = /defineConfig\s*\(\s*\{([^}]*)\}\s*\)/s;
|
|
359
|
+
const defineMatch = updated.match(defineConfigRegex);
|
|
360
|
+
if (defineMatch) {
|
|
361
|
+
const configContent = defineMatch[1].trim();
|
|
362
|
+
const newConfigContent = configContent ? `${configContent}
|
|
363
|
+
plugins: [wsx()],` : `plugins: [wsx()],`;
|
|
364
|
+
updated = updated.replace(
|
|
365
|
+
defineConfigRegex,
|
|
366
|
+
`defineConfig({
|
|
367
|
+
${newConfigContent}
|
|
368
|
+
})`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return updated;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/config/configure-eslint.ts
|
|
377
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
378
|
+
import { join as join3 } from "path";
|
|
379
|
+
async function configureESLint(projectRoot, options = {}) {
|
|
380
|
+
const flatConfigPath = join3(projectRoot, "eslint.config.js");
|
|
381
|
+
const flatConfigMjsPath = join3(projectRoot, "eslint.config.mjs");
|
|
382
|
+
const legacyConfigPath = join3(projectRoot, ".eslintrc.js");
|
|
383
|
+
const legacyConfigJsonPath = join3(projectRoot, ".eslintrc.json");
|
|
384
|
+
const hasFlatConfig = existsSync3(flatConfigPath) || existsSync3(flatConfigMjsPath);
|
|
385
|
+
const hasLegacyConfig = existsSync3(legacyConfigPath) || existsSync3(legacyConfigJsonPath);
|
|
386
|
+
const useFlatConfig = options.useFlatConfig ?? (hasFlatConfig || !hasLegacyConfig);
|
|
387
|
+
if (useFlatConfig) {
|
|
388
|
+
return await configureFlatESLint(projectRoot);
|
|
389
|
+
} else {
|
|
390
|
+
return await configureLegacyESLint(projectRoot);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async function configureFlatESLint(projectRoot) {
|
|
394
|
+
const configPath = existsSync3(join3(projectRoot, "eslint.config.js")) ? join3(projectRoot, "eslint.config.js") : join3(projectRoot, "eslint.config.mjs");
|
|
395
|
+
if (existsSync3(configPath)) {
|
|
396
|
+
try {
|
|
397
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
398
|
+
const hasWsxImport = content.includes("@wsxjs/eslint-plugin-wsx");
|
|
399
|
+
const hasWsxPlugin = content.includes("wsx:") || content.includes("wsxPlugin");
|
|
400
|
+
if (hasWsxImport && hasWsxPlugin) {
|
|
401
|
+
return {
|
|
402
|
+
success: true,
|
|
403
|
+
message: "eslint.config \u5DF2\u5305\u542B WSX \u63D2\u4EF6",
|
|
404
|
+
created: false
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const updated = addWsxPluginToFlatConfig(content);
|
|
408
|
+
writeFileSync3(configPath, updated, "utf-8");
|
|
409
|
+
return {
|
|
410
|
+
success: true,
|
|
411
|
+
message: `\u5DF2\u66F4\u65B0 ${configPath}`,
|
|
412
|
+
created: false
|
|
413
|
+
};
|
|
414
|
+
} catch (error) {
|
|
415
|
+
return {
|
|
416
|
+
success: false,
|
|
417
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6/\u66F4\u65B0 eslint.config: ${error}`,
|
|
418
|
+
created: false
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
const configContent = `import wsxPlugin from "@wsxjs/eslint-plugin-wsx";
|
|
423
|
+
|
|
424
|
+
export default [
|
|
425
|
+
{
|
|
426
|
+
files: ["**/*.{ts,tsx,js,jsx,wsx}"],
|
|
427
|
+
plugins: { wsx: wsxPlugin },
|
|
428
|
+
rules: {
|
|
429
|
+
"wsx/no-react-imports": "error",
|
|
430
|
+
"wsx/render-method-required": "error",
|
|
431
|
+
"wsx/state-requires-initial-value": "error",
|
|
432
|
+
"wsx/require-jsx-import-source": "error",
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
];
|
|
436
|
+
`;
|
|
437
|
+
writeFileSync3(configPath, configContent, "utf-8");
|
|
438
|
+
return {
|
|
439
|
+
success: true,
|
|
440
|
+
message: `\u5DF2\u521B\u5EFA ${configPath}`,
|
|
441
|
+
created: true
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function configureLegacyESLint(projectRoot) {
|
|
446
|
+
const configPath = existsSync3(join3(projectRoot, ".eslintrc.js")) ? join3(projectRoot, ".eslintrc.js") : join3(projectRoot, ".eslintrc.json");
|
|
447
|
+
if (existsSync3(configPath)) {
|
|
448
|
+
try {
|
|
449
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
450
|
+
const hasWsxPlugin = content.includes("@wsxjs/eslint-plugin-wsx") || content.includes("@wsxjs/wsx");
|
|
451
|
+
if (hasWsxPlugin) {
|
|
452
|
+
return {
|
|
453
|
+
success: true,
|
|
454
|
+
message: ".eslintrc \u5DF2\u5305\u542B WSX \u63D2\u4EF6",
|
|
455
|
+
created: false
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
success: false,
|
|
460
|
+
message: "\u68C0\u6D4B\u5230 Legacy ESLint \u914D\u7F6E\uFF0C\u5EFA\u8BAE\u8FC1\u79FB\u5230 Flat Config \u683C\u5F0F",
|
|
461
|
+
created: false
|
|
462
|
+
};
|
|
463
|
+
} catch (error) {
|
|
464
|
+
return {
|
|
465
|
+
success: false,
|
|
466
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6/\u66F4\u65B0 .eslintrc: ${error}`,
|
|
467
|
+
created: false
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
const configContent = `module.exports = {
|
|
472
|
+
plugins: ["@wsxjs/wsx"],
|
|
473
|
+
rules: {
|
|
474
|
+
"@wsxjs/wsx/no-react-imports": "error",
|
|
475
|
+
"@wsxjs/wsx/render-method-required": "error",
|
|
476
|
+
"@wsxjs/wsx/state-requires-initial-value": "error",
|
|
477
|
+
},
|
|
478
|
+
};
|
|
479
|
+
`;
|
|
480
|
+
writeFileSync3(join3(projectRoot, ".eslintrc.js"), configContent, "utf-8");
|
|
481
|
+
return {
|
|
482
|
+
success: true,
|
|
483
|
+
message: "\u5DF2\u521B\u5EFA .eslintrc.js\uFF08\u5EFA\u8BAE\u4F7F\u7528 Flat Config\uFF09",
|
|
484
|
+
created: true
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function addWsxPluginToFlatConfig(content) {
|
|
489
|
+
const hasWsxImport = content.includes("@wsxjs/eslint-plugin-wsx");
|
|
490
|
+
let updated = content;
|
|
491
|
+
if (!hasWsxImport) {
|
|
492
|
+
const importRegex = /^import\s+.*?from\s+['"].*?['"];?$/gm;
|
|
493
|
+
const imports = content.match(importRegex);
|
|
494
|
+
if (imports && imports.length > 0) {
|
|
495
|
+
const lastImport = imports[imports.length - 1];
|
|
496
|
+
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
497
|
+
const insertIndex = lastImportIndex + lastImport.length;
|
|
498
|
+
const wsxImport = `import wsxPlugin from "@wsxjs/eslint-plugin-wsx";
|
|
499
|
+
`;
|
|
500
|
+
updated = content.slice(0, insertIndex) + "\n" + wsxImport + content.slice(insertIndex);
|
|
501
|
+
} else {
|
|
502
|
+
updated = `import wsxPlugin from "@wsxjs/eslint-plugin-wsx";
|
|
503
|
+
|
|
504
|
+
` + content;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (!updated.includes("wsx:") && !updated.includes("wsxPlugin")) {
|
|
508
|
+
const configObjectRegex = /\{\s*files:\s*\[([^\]]+)\],([^}]*)\}/s;
|
|
509
|
+
const match = updated.match(configObjectRegex);
|
|
510
|
+
if (match) {
|
|
511
|
+
const configContent = match[2].trim();
|
|
512
|
+
let newConfigContent = configContent;
|
|
513
|
+
if (!configContent.includes("plugins:")) {
|
|
514
|
+
newConfigContent = `plugins: { wsx: wsxPlugin },
|
|
515
|
+
${newConfigContent}`;
|
|
516
|
+
} else {
|
|
517
|
+
const pluginsRegex = /plugins:\s*\{([^}]*)\}/s;
|
|
518
|
+
const pluginsMatch = newConfigContent.match(pluginsRegex);
|
|
519
|
+
if (pluginsMatch) {
|
|
520
|
+
const pluginsContent = pluginsMatch[1].trim();
|
|
521
|
+
newConfigContent = newConfigContent.replace(
|
|
522
|
+
pluginsRegex,
|
|
523
|
+
`plugins: { ${pluginsContent}
|
|
524
|
+
wsx: wsxPlugin,
|
|
525
|
+
}`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (!configContent.includes("rules:")) {
|
|
530
|
+
newConfigContent = `${newConfigContent}
|
|
531
|
+
rules: {
|
|
532
|
+
"wsx/no-react-imports": "error",
|
|
533
|
+
"wsx/render-method-required": "error",
|
|
534
|
+
"wsx/state-requires-initial-value": "error",
|
|
535
|
+
"wsx/require-jsx-import-source": "error",
|
|
536
|
+
},`;
|
|
537
|
+
} else {
|
|
538
|
+
const rulesRegex = /rules:\s*\{([^}]*)\}/s;
|
|
539
|
+
const rulesMatch = newConfigContent.match(rulesRegex);
|
|
540
|
+
if (rulesMatch) {
|
|
541
|
+
const rulesContent = rulesMatch[1].trim();
|
|
542
|
+
const newRules = `"wsx/no-react-imports": "error",
|
|
543
|
+
"wsx/render-method-required": "error",
|
|
544
|
+
"wsx/state-requires-initial-value": "error",
|
|
545
|
+
"wsx/require-jsx-import-source": "error",`;
|
|
546
|
+
newConfigContent = newConfigContent.replace(
|
|
547
|
+
rulesRegex,
|
|
548
|
+
`rules: {
|
|
549
|
+
${newRules}
|
|
550
|
+
${rulesContent}
|
|
551
|
+
}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
updated = updated.replace(
|
|
556
|
+
configObjectRegex,
|
|
557
|
+
`{
|
|
558
|
+
files: [${match[1]}],
|
|
559
|
+
${newConfigContent}
|
|
560
|
+
}`
|
|
561
|
+
);
|
|
562
|
+
} else {
|
|
563
|
+
const exportDefaultRegex = /export\s+default\s*\[([^\]]*)\]/s;
|
|
564
|
+
const exportMatch = updated.match(exportDefaultRegex);
|
|
565
|
+
if (exportMatch) {
|
|
566
|
+
const existingConfigs = exportMatch[1].trim();
|
|
567
|
+
const newConfig = ` {
|
|
568
|
+
files: ["**/*.{ts,tsx,js,jsx,wsx}"],
|
|
569
|
+
plugins: { wsx: wsxPlugin },
|
|
570
|
+
rules: {
|
|
571
|
+
"wsx/no-react-imports": "error",
|
|
572
|
+
"wsx/render-method-required": "error",
|
|
573
|
+
"wsx/state-requires-initial-value": "error",
|
|
574
|
+
"wsx/require-jsx-import-source": "error",
|
|
575
|
+
},
|
|
576
|
+
},`;
|
|
577
|
+
updated = updated.replace(
|
|
578
|
+
exportDefaultRegex,
|
|
579
|
+
`export default [
|
|
580
|
+
${existingConfigs ? existingConfigs + ",\n " : ""}${newConfig}
|
|
581
|
+
]`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return updated;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// src/commands/init.ts
|
|
590
|
+
async function initWSX(options = {}) {
|
|
591
|
+
const projectRoot = process.cwd();
|
|
592
|
+
let finalOptions = { ...options };
|
|
593
|
+
if (options.interactive !== false) {
|
|
594
|
+
finalOptions = await promptUserOptions(options);
|
|
595
|
+
}
|
|
596
|
+
const results = [];
|
|
597
|
+
let resolveComplete;
|
|
598
|
+
const completePromise = new Promise((resolve) => {
|
|
599
|
+
resolveComplete = resolve;
|
|
600
|
+
});
|
|
601
|
+
const configSteps = [
|
|
602
|
+
{
|
|
603
|
+
name: "TypeScript",
|
|
604
|
+
skip: finalOptions.skipTsconfig ?? false,
|
|
605
|
+
execute: async () => {
|
|
606
|
+
const tsOptions = {
|
|
607
|
+
useTsConfigPackage: finalOptions.useTsconfigPackage,
|
|
608
|
+
useDecorators: finalOptions.useDecorators ?? true
|
|
609
|
+
};
|
|
610
|
+
const result = await configureTypeScript(projectRoot, tsOptions);
|
|
611
|
+
results.push({ name: "TypeScript", ...result });
|
|
612
|
+
return result;
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: "Vite",
|
|
617
|
+
skip: finalOptions.skipVite ?? false,
|
|
618
|
+
execute: async () => {
|
|
619
|
+
const result = await configureVite(projectRoot);
|
|
620
|
+
results.push({ name: "Vite", ...result });
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: "wsx.d.ts",
|
|
626
|
+
skip: finalOptions.skipTypes ?? false,
|
|
627
|
+
execute: async () => {
|
|
628
|
+
const result = await generateWsxTypes(projectRoot);
|
|
629
|
+
const stepResult = {
|
|
630
|
+
success: result.success,
|
|
631
|
+
message: result.message,
|
|
632
|
+
created: result.created
|
|
633
|
+
};
|
|
634
|
+
results.push({ name: "wsx.d.ts", ...stepResult });
|
|
635
|
+
return stepResult;
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
name: "ESLint",
|
|
640
|
+
skip: finalOptions.skipEslint ?? false,
|
|
641
|
+
execute: async () => {
|
|
642
|
+
const eslintOptions = {
|
|
643
|
+
useFlatConfig: void 0
|
|
644
|
+
// 自动检测
|
|
645
|
+
};
|
|
646
|
+
const result = await configureESLint(projectRoot, eslintOptions);
|
|
647
|
+
results.push({ name: "ESLint", ...result });
|
|
648
|
+
return result;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
];
|
|
652
|
+
const { unmount } = render(
|
|
653
|
+
React2.createElement(InitUI, {
|
|
654
|
+
onComplete: () => {
|
|
655
|
+
resolveComplete();
|
|
656
|
+
},
|
|
657
|
+
options: finalOptions,
|
|
658
|
+
// Keep for potential future use
|
|
659
|
+
configSteps
|
|
660
|
+
})
|
|
661
|
+
);
|
|
662
|
+
await completePromise;
|
|
663
|
+
unmount();
|
|
664
|
+
console.log("\n\u914D\u7F6E\u5B8C\u6210\u6458\u8981:");
|
|
665
|
+
results.forEach((result) => {
|
|
666
|
+
const status = result.success ? "\u2713" : "\u2717";
|
|
667
|
+
console.log(` ${status} ${result.name}: ${result.message}`);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
async function promptUserOptions(existingOptions) {
|
|
671
|
+
const answers = await inquirer.prompt([
|
|
672
|
+
{
|
|
673
|
+
type: "confirm",
|
|
674
|
+
name: "useDecorators",
|
|
675
|
+
message: "\u662F\u5426\u4F7F\u7528\u88C5\u9970\u5668\uFF08@state\uFF09\uFF1F",
|
|
676
|
+
default: existingOptions.useDecorators ?? true,
|
|
677
|
+
when: existingOptions.useDecorators === void 0
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
type: "confirm",
|
|
681
|
+
name: "useTsconfigPackage",
|
|
682
|
+
message: "\u662F\u5426\u4F7F\u7528 @wsxjs/wsx-tsconfig \u5305\uFF1F",
|
|
683
|
+
default: existingOptions.useTsconfigPackage ?? false,
|
|
684
|
+
when: existingOptions.useTsconfigPackage === void 0
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
type: "confirm",
|
|
688
|
+
name: "configureTsconfig",
|
|
689
|
+
message: "\u662F\u5426\u914D\u7F6E TypeScript\uFF1F",
|
|
690
|
+
default: !existingOptions.skipTsconfig,
|
|
691
|
+
when: existingOptions.skipTsconfig === void 0
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
type: "confirm",
|
|
695
|
+
name: "configureVite",
|
|
696
|
+
message: "\u662F\u5426\u914D\u7F6E Vite\uFF1F",
|
|
697
|
+
default: !existingOptions.skipVite,
|
|
698
|
+
when: existingOptions.skipVite === void 0
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
type: "confirm",
|
|
702
|
+
name: "configureEslint",
|
|
703
|
+
message: "\u662F\u5426\u914D\u7F6E ESLint\uFF1F",
|
|
704
|
+
default: !existingOptions.skipEslint,
|
|
705
|
+
when: existingOptions.skipEslint === void 0
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
type: "confirm",
|
|
709
|
+
name: "generateTypes",
|
|
710
|
+
message: "\u662F\u5426\u751F\u6210 wsx.d.ts\uFF1F",
|
|
711
|
+
default: !existingOptions.skipTypes,
|
|
712
|
+
when: existingOptions.skipTypes === void 0
|
|
713
|
+
}
|
|
714
|
+
]);
|
|
715
|
+
return {
|
|
716
|
+
...existingOptions,
|
|
717
|
+
useDecorators: answers.useDecorators ?? existingOptions.useDecorators,
|
|
718
|
+
useTsconfigPackage: answers.useTsconfigPackage ?? existingOptions.useTsconfigPackage,
|
|
719
|
+
skipTsconfig: !answers.configureTsconfig,
|
|
720
|
+
skipVite: !answers.configureVite,
|
|
721
|
+
skipEslint: !answers.configureEslint,
|
|
722
|
+
skipTypes: !answers.generateTypes
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
async function generateWsxTypes(projectRoot) {
|
|
726
|
+
const typesDir = join4(projectRoot, "src", "types");
|
|
727
|
+
const wsxDtsPath = join4(typesDir, "wsx.d.ts");
|
|
728
|
+
if (existsSync4(wsxDtsPath)) {
|
|
729
|
+
return {
|
|
730
|
+
success: true,
|
|
731
|
+
message: "wsx.d.ts \u5DF2\u5B58\u5728",
|
|
732
|
+
created: false
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (!existsSync4(typesDir)) {
|
|
736
|
+
mkdirSync(typesDir, { recursive: true });
|
|
737
|
+
}
|
|
738
|
+
const wsxDtsContent = `// Type declaration for .wsx files
|
|
739
|
+
// Auto-generated by @wsxjs/cli
|
|
740
|
+
// You can modify this file if needed
|
|
741
|
+
|
|
742
|
+
declare module "*.wsx" {
|
|
743
|
+
import { WebComponent, LightComponent } from "@wsxjs/wsx-core";
|
|
744
|
+
const Component: new (...args: unknown[]) => WebComponent | LightComponent;
|
|
745
|
+
export default Component;
|
|
746
|
+
}
|
|
747
|
+
`;
|
|
748
|
+
writeFileSync4(wsxDtsPath, wsxDtsContent, "utf-8");
|
|
749
|
+
return {
|
|
750
|
+
success: true,
|
|
751
|
+
message: "\u5DF2\u751F\u6210 wsx.d.ts",
|
|
752
|
+
created: true
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/commands/check-config.ts
|
|
757
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
758
|
+
import { join as join5 } from "path";
|
|
759
|
+
import chalk from "chalk";
|
|
760
|
+
async function checkConfig(projectRoot = process.cwd()) {
|
|
761
|
+
const result = {
|
|
762
|
+
hasWsxTypes: false,
|
|
763
|
+
hasTsConfig: false,
|
|
764
|
+
hasViteConfig: false,
|
|
765
|
+
tsConfigValid: false,
|
|
766
|
+
viteConfigValid: false,
|
|
767
|
+
issues: [],
|
|
768
|
+
suggestions: []
|
|
769
|
+
};
|
|
770
|
+
const wsxDtsPath = join5(projectRoot, "src", "types", "wsx.d.ts");
|
|
771
|
+
result.hasWsxTypes = existsSync5(wsxDtsPath);
|
|
772
|
+
if (!result.hasWsxTypes) {
|
|
773
|
+
result.issues.push("Missing src/types/wsx.d.ts");
|
|
774
|
+
result.suggestions.push("Run: npx wsx init");
|
|
775
|
+
}
|
|
776
|
+
const tsconfigPath = join5(projectRoot, "tsconfig.json");
|
|
777
|
+
result.hasTsConfig = existsSync5(tsconfigPath);
|
|
778
|
+
if (result.hasTsConfig) {
|
|
779
|
+
try {
|
|
780
|
+
const tsconfigContent = readFileSync4(tsconfigPath, "utf-8");
|
|
781
|
+
const tsconfig = JSON.parse(tsconfigContent);
|
|
782
|
+
const compilerOptions = tsconfig.compilerOptions || {};
|
|
783
|
+
result.tsConfigValid = true;
|
|
784
|
+
if (compilerOptions.jsx !== "react-jsx") {
|
|
785
|
+
result.issues.push('tsconfig.json: jsx should be "react-jsx"');
|
|
786
|
+
result.suggestions.push(
|
|
787
|
+
'Add to tsconfig.json: "compilerOptions": { "jsx": "react-jsx" }'
|
|
788
|
+
);
|
|
789
|
+
result.tsConfigValid = false;
|
|
790
|
+
}
|
|
791
|
+
if (compilerOptions.jsxImportSource !== "@wsxjs/wsx-core") {
|
|
792
|
+
result.issues.push('tsconfig.json: jsxImportSource should be "@wsxjs/wsx-core"');
|
|
793
|
+
result.suggestions.push(
|
|
794
|
+
'Add to tsconfig.json: "compilerOptions": { "jsxImportSource": "@wsxjs/wsx-core" }'
|
|
795
|
+
);
|
|
796
|
+
result.tsConfigValid = false;
|
|
797
|
+
}
|
|
798
|
+
if (compilerOptions.experimentalDecorators !== true) {
|
|
799
|
+
result.suggestions.push(
|
|
800
|
+
"Recommended: Enable experimentalDecorators for @state decorator support"
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
if (compilerOptions.useDefineForClassFields !== false) {
|
|
804
|
+
result.suggestions.push(
|
|
805
|
+
"Recommended: Set useDefineForClassFields to false for @state decorator support"
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
} catch {
|
|
809
|
+
result.issues.push("tsconfig.json: Invalid JSON format");
|
|
810
|
+
result.tsConfigValid = false;
|
|
811
|
+
}
|
|
812
|
+
} else {
|
|
813
|
+
result.issues.push("Missing tsconfig.json");
|
|
814
|
+
result.suggestions.push("Create a tsconfig.json with WSX-compatible settings");
|
|
815
|
+
}
|
|
816
|
+
const viteConfigPaths = [
|
|
817
|
+
join5(projectRoot, "vite.config.ts"),
|
|
818
|
+
join5(projectRoot, "vite.config.js"),
|
|
819
|
+
join5(projectRoot, "vite.config.mts"),
|
|
820
|
+
join5(projectRoot, "vite.config.mjs")
|
|
821
|
+
];
|
|
822
|
+
for (const viteConfigPath of viteConfigPaths) {
|
|
823
|
+
if (existsSync5(viteConfigPath)) {
|
|
824
|
+
result.hasViteConfig = true;
|
|
825
|
+
try {
|
|
826
|
+
const viteConfigContent = readFileSync4(viteConfigPath, "utf-8");
|
|
827
|
+
const hasWsxImport = viteConfigContent.includes("@wsxjs/wsx-vite-plugin");
|
|
828
|
+
const hasWsxPlugin = viteConfigContent.includes("wsx()");
|
|
829
|
+
if (!hasWsxImport || !hasWsxPlugin) {
|
|
830
|
+
result.issues.push("vite.config: Missing @wsxjs/wsx-vite-plugin");
|
|
831
|
+
result.suggestions.push(
|
|
832
|
+
'Add to vite.config: import { wsx } from "@wsxjs/wsx-vite-plugin" and use wsx() in plugins array'
|
|
833
|
+
);
|
|
834
|
+
result.viteConfigValid = false;
|
|
835
|
+
} else {
|
|
836
|
+
result.viteConfigValid = true;
|
|
837
|
+
}
|
|
838
|
+
} catch {
|
|
839
|
+
result.issues.push("vite.config: Unable to read file");
|
|
840
|
+
}
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (!result.hasViteConfig) {
|
|
845
|
+
result.suggestions.push("Recommended: Create vite.config.ts with @wsxjs/wsx-vite-plugin");
|
|
846
|
+
}
|
|
847
|
+
return result;
|
|
848
|
+
}
|
|
849
|
+
async function displayCheckResults(result) {
|
|
850
|
+
console.log(chalk.blue.bold("\n\u{1F50D} WSX Configuration Check\n"));
|
|
851
|
+
console.log(chalk.bold("Status:"));
|
|
852
|
+
console.log(
|
|
853
|
+
` wsx.d.ts: ${result.hasWsxTypes ? chalk.green("\u2713 Found") : chalk.red("\u2717 Missing")}`
|
|
854
|
+
);
|
|
855
|
+
console.log(
|
|
856
|
+
` tsconfig.json: ${result.hasTsConfig ? result.tsConfigValid ? chalk.green("\u2713 Valid") : chalk.yellow("\u26A0 Needs update") : chalk.red("\u2717 Missing")}`
|
|
857
|
+
);
|
|
858
|
+
console.log(
|
|
859
|
+
` vite.config: ${result.hasViteConfig ? result.viteConfigValid ? chalk.green("\u2713 Valid") : chalk.yellow("\u26A0 Needs update") : chalk.gray("- Not found")}`
|
|
860
|
+
);
|
|
861
|
+
if (result.issues.length > 0) {
|
|
862
|
+
console.log(chalk.red.bold("\n\u274C Issues:"));
|
|
863
|
+
result.issues.forEach((issue) => {
|
|
864
|
+
console.log(chalk.red(` \u2022 ${issue}`));
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
if (result.suggestions.length > 0) {
|
|
868
|
+
console.log(chalk.yellow.bold("\n\u{1F4A1} Suggestions:"));
|
|
869
|
+
result.suggestions.forEach((suggestion) => {
|
|
870
|
+
console.log(chalk.yellow(` \u2022 ${suggestion}`));
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
if (result.issues.length === 0) {
|
|
874
|
+
console.log(
|
|
875
|
+
chalk.green.bold("\n\u2705 All checks passed! Your WSX configuration looks good.\n")
|
|
876
|
+
);
|
|
877
|
+
} else {
|
|
878
|
+
console.log(chalk.yellow.bold("\n\u26A0\uFE0F Some issues need attention.\n"));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/index.ts
|
|
883
|
+
var program = new Command();
|
|
884
|
+
program.name("wsx").description("WSXJS CLI tool").version("0.0.17");
|
|
885
|
+
program.command("init").description("Initialize WSXJS in your project").option("--skip-tsconfig", "Skip TypeScript configuration").option("--skip-vite", "Skip Vite configuration").option("--skip-eslint", "Skip ESLint configuration").option("--skip-types", "Skip wsx.d.ts generation").option("--use-tsconfig-package", "Use @wsxjs/wsx-tsconfig package").option("--use-decorators", "Enable decorator support (@state)").option("--no-interactive", "Skip interactive prompts").action(async (options) => {
|
|
886
|
+
await initWSX(options);
|
|
887
|
+
});
|
|
888
|
+
program.command("check").description("Check WSX configuration").action(async () => {
|
|
889
|
+
const result = await checkConfig();
|
|
890
|
+
await displayCheckResults(result);
|
|
891
|
+
});
|
|
892
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wsxjs/cli",
|
|
3
|
+
"version": "0.0.18",
|
|
4
|
+
"description": "CLI tool for WSXJS initialization and configuration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"wsx": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/wsxjs/wsxjs.git",
|
|
14
|
+
"directory": "packages/cli"
|
|
15
|
+
},
|
|
16
|
+
"author": "Albert Li <albert.li@wsxjs.com>",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"wsx",
|
|
24
|
+
"cli",
|
|
25
|
+
"web-components",
|
|
26
|
+
"typescript",
|
|
27
|
+
"init"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^5.3.0",
|
|
31
|
+
"commander": "^11.0.0",
|
|
32
|
+
"ink": "^4.4.1",
|
|
33
|
+
"ink-spinner": "^5.0.0",
|
|
34
|
+
"inquirer": "^10.2.2",
|
|
35
|
+
"react": "^18.2.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/inquirer": "^9.0.0",
|
|
39
|
+
"@types/jest": "^29.0.0",
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"@types/react": "^18.2.0",
|
|
42
|
+
"jest": "^29.0.0",
|
|
43
|
+
"ts-jest": "^29.0.0",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
52
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
53
|
+
"test": "jest",
|
|
54
|
+
"test:watch": "jest --watch",
|
|
55
|
+
"test:coverage": "jest --coverage",
|
|
56
|
+
"lint": "eslint .",
|
|
57
|
+
"lint:fix": "eslint . --fix",
|
|
58
|
+
"typecheck": "tsc --noEmit"
|
|
59
|
+
}
|
|
60
|
+
}
|