nuxt-gin-tools 0.1.21 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.go-watch.json +20 -0
- package/package.json +2 -1
- package/src/dev-go.d.ts +1 -0
- package/src/dev-go.js +374 -0
- package/src/develop.js +113 -16
- package/src/postinstall.js +0 -16
- package/src/server-config.json +11 -1
- package/.air.toml +0 -42
package/.go-watch.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tmpDir": ".build/.server",
|
|
3
|
+
"testDataDir": "testdata",
|
|
4
|
+
"includeExt": ["go", "tpl", "html"],
|
|
5
|
+
"includeDir": [],
|
|
6
|
+
"includeFile": [],
|
|
7
|
+
"excludeDir": [
|
|
8
|
+
"assets",
|
|
9
|
+
".build/.server",
|
|
10
|
+
"vendor",
|
|
11
|
+
"testdata",
|
|
12
|
+
"node_modules",
|
|
13
|
+
"vue",
|
|
14
|
+
"api",
|
|
15
|
+
".vscode",
|
|
16
|
+
".git"
|
|
17
|
+
],
|
|
18
|
+
"excludeFile": [],
|
|
19
|
+
"excludeRegex": ["_test.go"]
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-gin-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "This project is used as a dependency for [nuxt-gin-starter](https://github.com/RapboyGao/nuxt-gin-starter.git)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nuxt-gin": "index.js"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"7zip-min": "^2.1.0",
|
|
17
17
|
"chalk": "^5.4.1",
|
|
18
|
+
"chokidar": "^3.6.0",
|
|
18
19
|
"concurrently": "^9.2.0",
|
|
19
20
|
"fast-glob": "^3.3.3",
|
|
20
21
|
"fs-extra": "^11.3.0"
|
package/src/dev-go.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/src/dev-go.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const child_process_1 = require("child_process");
|
|
16
|
+
const child_process_2 = require("child_process");
|
|
17
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
18
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
19
|
+
const fs_extra_1 = require("fs-extra");
|
|
20
|
+
const os_1 = __importDefault(require("os"));
|
|
21
|
+
const path_1 = require("path");
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const RESTART_DEBOUNCE_MS = 150;
|
|
24
|
+
const SHUTDOWN_TIMEOUT_MS = 2000;
|
|
25
|
+
const LOG_TAG = "go-watch";
|
|
26
|
+
const serverConfigPath = (0, path_1.join)(cwd, "server.config.json");
|
|
27
|
+
const ginPort = getGinPort();
|
|
28
|
+
function getGinPort() {
|
|
29
|
+
if (!(0, fs_extra_1.existsSync)(serverConfigPath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const serverConfig = (0, fs_extra_1.readJSONSync)(serverConfigPath);
|
|
34
|
+
if (Number.isInteger(serverConfig.ginPort) && serverConfig.ginPort > 0) {
|
|
35
|
+
return serverConfig.ginPort;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
// best effort
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function killPortUnix(port) {
|
|
44
|
+
try {
|
|
45
|
+
const output = (0, child_process_2.execSync)(`lsof -ti tcp:${port}`, {
|
|
46
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
47
|
+
})
|
|
48
|
+
.toString()
|
|
49
|
+
.trim();
|
|
50
|
+
if (!output) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const pids = output
|
|
54
|
+
.split("\n")
|
|
55
|
+
.map((pid) => Number(pid.trim()))
|
|
56
|
+
.filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
57
|
+
for (const pid of pids) {
|
|
58
|
+
try {
|
|
59
|
+
process.kill(pid, "SIGKILL");
|
|
60
|
+
console.log(chalk_1.default.yellow(`[${LOG_TAG}] killed process ${pid} on ginPort ${port} (unix)`));
|
|
61
|
+
}
|
|
62
|
+
catch (_a) {
|
|
63
|
+
// best effort
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (_b) {
|
|
68
|
+
// best effort
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function killPortWindows(port) {
|
|
72
|
+
try {
|
|
73
|
+
const output = (0, child_process_2.execSync)(`netstat -ano -p tcp`, {
|
|
74
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
75
|
+
})
|
|
76
|
+
.toString()
|
|
77
|
+
.trim();
|
|
78
|
+
if (!output) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const pids = new Set();
|
|
82
|
+
for (const line of output.split("\n")) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed || !trimmed.startsWith("TCP")) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const parts = trimmed.split(/\s+/);
|
|
88
|
+
if (parts.length < 5) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const localAddress = parts[1];
|
|
92
|
+
const pid = Number(parts[parts.length - 1]);
|
|
93
|
+
const match = localAddress.match(/:(\d+)$/);
|
|
94
|
+
if (!match) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const localPort = Number(match[1]);
|
|
98
|
+
if (localPort === port && Number.isInteger(pid) && pid > 0) {
|
|
99
|
+
pids.add(pid);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const pid of pids) {
|
|
103
|
+
try {
|
|
104
|
+
(0, child_process_2.execSync)(`taskkill /PID ${pid} /F`, {
|
|
105
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
106
|
+
});
|
|
107
|
+
console.log(chalk_1.default.yellow(`[${LOG_TAG}] killed process ${pid} on ginPort ${port} (win32)`));
|
|
108
|
+
}
|
|
109
|
+
catch (_a) {
|
|
110
|
+
// best effort
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (_b) {
|
|
115
|
+
// best effort
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function killGinPortIfNeeded() {
|
|
119
|
+
if (!ginPort) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (os_1.default.platform() === "win32") {
|
|
123
|
+
killPortWindows(ginPort);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
killPortUnix(ginPort);
|
|
127
|
+
}
|
|
128
|
+
function normalizePath(filePath) {
|
|
129
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
130
|
+
}
|
|
131
|
+
function toProjectRelative(filePath) {
|
|
132
|
+
const abs = (0, path_1.isAbsolute)(filePath) ? filePath : (0, path_1.resolve)(cwd, filePath);
|
|
133
|
+
return normalizePath((0, path_1.relative)(cwd, abs));
|
|
134
|
+
}
|
|
135
|
+
function toStringArray(value) {
|
|
136
|
+
if (!Array.isArray(value)) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
return value
|
|
140
|
+
.filter((item) => typeof item === "string")
|
|
141
|
+
.map((item) => item.trim())
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
}
|
|
144
|
+
function toStringValue(value) {
|
|
145
|
+
if (typeof value !== "string") {
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
return value.trim();
|
|
149
|
+
}
|
|
150
|
+
function loadWatchConfig() {
|
|
151
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
152
|
+
const defaultConfig = {
|
|
153
|
+
includeExt: new Set(["go"]),
|
|
154
|
+
includeDir: [],
|
|
155
|
+
includeFile: new Set(),
|
|
156
|
+
excludeDir: [".git", "node_modules", "vendor", "vue"],
|
|
157
|
+
excludeFile: new Set(),
|
|
158
|
+
excludeRegex: [/_test\.go$/],
|
|
159
|
+
tmpDir: ".build/.server",
|
|
160
|
+
testDataDir: "testdata",
|
|
161
|
+
};
|
|
162
|
+
const candidates = [
|
|
163
|
+
process.env.NUXT_GIN_WATCH_CONFIG,
|
|
164
|
+
(0, path_1.join)(cwd, "node_modules/nuxt-gin-tools/.go-watch.json"),
|
|
165
|
+
(0, path_1.join)(cwd, ".go-watch.json"),
|
|
166
|
+
(0, path_1.join)(__dirname, "..", ".go-watch.json"),
|
|
167
|
+
(0, path_1.join)(__dirname, "..", "..", ".go-watch.json"),
|
|
168
|
+
].filter((item) => Boolean(item));
|
|
169
|
+
const configPath = candidates.find((item) => (0, fs_extra_1.existsSync)(item));
|
|
170
|
+
if (!configPath) {
|
|
171
|
+
return defaultConfig;
|
|
172
|
+
}
|
|
173
|
+
let parsedConfig = {};
|
|
174
|
+
try {
|
|
175
|
+
parsedConfig = JSON.parse((0, fs_extra_1.readFileSync)(configPath, "utf-8"));
|
|
176
|
+
}
|
|
177
|
+
catch (_j) {
|
|
178
|
+
console.warn(chalk_1.default.yellow(`[${LOG_TAG}] invalid watch config JSON, fallback to defaults: ${configPath}`));
|
|
179
|
+
return defaultConfig;
|
|
180
|
+
}
|
|
181
|
+
const includeExt = toStringArray((_a = parsedConfig.includeExt) !== null && _a !== void 0 ? _a : parsedConfig.include_ext)
|
|
182
|
+
.map((item) => item.replace(/^\./, ""))
|
|
183
|
+
.filter((item) => /^[a-zA-Z0-9]+$/.test(item));
|
|
184
|
+
const includeDir = toStringArray((_b = parsedConfig.includeDir) !== null && _b !== void 0 ? _b : parsedConfig.include_dir)
|
|
185
|
+
.map((item) => normalizePath(item))
|
|
186
|
+
.filter(Boolean);
|
|
187
|
+
const includeFile = toStringArray((_c = parsedConfig.includeFile) !== null && _c !== void 0 ? _c : parsedConfig.include_file)
|
|
188
|
+
.map((item) => normalizePath(item))
|
|
189
|
+
.filter(Boolean);
|
|
190
|
+
const excludeDir = toStringArray((_d = parsedConfig.excludeDir) !== null && _d !== void 0 ? _d : parsedConfig.exclude_dir)
|
|
191
|
+
.map((item) => normalizePath(item))
|
|
192
|
+
.filter(Boolean);
|
|
193
|
+
const excludeFile = toStringArray((_e = parsedConfig.excludeFile) !== null && _e !== void 0 ? _e : parsedConfig.exclude_file)
|
|
194
|
+
.map((item) => normalizePath(item))
|
|
195
|
+
.filter(Boolean);
|
|
196
|
+
const excludeRegex = toStringArray((_f = parsedConfig.excludeRegex) !== null && _f !== void 0 ? _f : parsedConfig.exclude_regex)
|
|
197
|
+
.map((item) => {
|
|
198
|
+
try {
|
|
199
|
+
return new RegExp(item);
|
|
200
|
+
}
|
|
201
|
+
catch (_a) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
.filter((item) => item instanceof RegExp);
|
|
206
|
+
const tmpDir = normalizePath(toStringValue((_g = parsedConfig.tmpDir) !== null && _g !== void 0 ? _g : parsedConfig.tmp_dir) || defaultConfig.tmpDir);
|
|
207
|
+
const testdataDir = normalizePath(toStringValue((_h = parsedConfig.testDataDir) !== null && _h !== void 0 ? _h : parsedConfig.testdata_dir) ||
|
|
208
|
+
defaultConfig.testDataDir);
|
|
209
|
+
return {
|
|
210
|
+
includeExt: new Set(includeExt.length ? includeExt : [...defaultConfig.includeExt]),
|
|
211
|
+
includeDir,
|
|
212
|
+
includeFile: new Set(includeFile),
|
|
213
|
+
excludeDir: [...new Set([...defaultConfig.excludeDir, ...excludeDir, tmpDir, testdataDir])],
|
|
214
|
+
excludeFile: new Set(excludeFile),
|
|
215
|
+
excludeRegex: excludeRegex.length ? excludeRegex : defaultConfig.excludeRegex,
|
|
216
|
+
tmpDir,
|
|
217
|
+
testDataDir: testdataDir,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function pathInDir(relPath, dir) {
|
|
221
|
+
const normalizedDir = normalizePath(dir).replace(/\/+$/, "");
|
|
222
|
+
return relPath === normalizedDir || relPath.startsWith(`${normalizedDir}/`);
|
|
223
|
+
}
|
|
224
|
+
function shouldIgnore(relPath, config) {
|
|
225
|
+
if (!relPath || relPath === ".") {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
if (config.excludeFile.has(relPath)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (config.excludeDir.some((dir) => pathInDir(relPath, dir))) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
return config.excludeRegex.some((reg) => reg.test(relPath));
|
|
235
|
+
}
|
|
236
|
+
function shouldTrigger(relPath, config) {
|
|
237
|
+
if (shouldIgnore(relPath, config)) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
const ext = (0, path_1.extname)(relPath).replace(/^\./, "");
|
|
241
|
+
const inIncludedFile = config.includeFile.has(relPath);
|
|
242
|
+
if (config.includeDir.length > 0) {
|
|
243
|
+
const inIncludedDir = config.includeDir.some((dir) => pathInDir(relPath, dir));
|
|
244
|
+
if (!inIncludedDir && !inIncludedFile) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (inIncludedFile) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
if (!ext) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
return config.includeExt.has(ext);
|
|
255
|
+
}
|
|
256
|
+
function quote(arg) {
|
|
257
|
+
if (/\s/.test(arg)) {
|
|
258
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
259
|
+
}
|
|
260
|
+
return arg;
|
|
261
|
+
}
|
|
262
|
+
function runGoProcess() {
|
|
263
|
+
const command = `go run ${quote("main.go")}`;
|
|
264
|
+
killGinPortIfNeeded();
|
|
265
|
+
console.log(chalk_1.default.green(`[${LOG_TAG}] start: ${command}`));
|
|
266
|
+
return (0, child_process_1.spawn)(command, {
|
|
267
|
+
cwd,
|
|
268
|
+
shell: true,
|
|
269
|
+
stdio: "inherit",
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function stopGoProcess(proc) {
|
|
273
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
274
|
+
if (!proc || proc.killed || proc.exitCode !== null) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
yield new Promise((resolveStop) => {
|
|
278
|
+
let finished = false;
|
|
279
|
+
const done = () => {
|
|
280
|
+
if (finished) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
finished = true;
|
|
284
|
+
resolveStop();
|
|
285
|
+
};
|
|
286
|
+
const timer = setTimeout(() => {
|
|
287
|
+
try {
|
|
288
|
+
proc.kill("SIGKILL");
|
|
289
|
+
}
|
|
290
|
+
catch (_a) {
|
|
291
|
+
// best effort
|
|
292
|
+
}
|
|
293
|
+
done();
|
|
294
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
295
|
+
proc.once("exit", () => {
|
|
296
|
+
clearTimeout(timer);
|
|
297
|
+
done();
|
|
298
|
+
});
|
|
299
|
+
try {
|
|
300
|
+
proc.kill("SIGTERM");
|
|
301
|
+
}
|
|
302
|
+
catch (_a) {
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
done();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function start() {
|
|
310
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
311
|
+
const watchConfig = loadWatchConfig();
|
|
312
|
+
const watchRoots = watchConfig.includeDir.length
|
|
313
|
+
? watchConfig.includeDir.map((dir) => (0, path_1.join)(cwd, dir))
|
|
314
|
+
: [cwd];
|
|
315
|
+
console.log(chalk_1.default.cyan(`[${LOG_TAG}] watching: ${watchRoots.map((item) => toProjectRelative(item)).join(", ")}`));
|
|
316
|
+
let restarting = false;
|
|
317
|
+
let goProc = runGoProcess();
|
|
318
|
+
let restartTimer = null;
|
|
319
|
+
const watcher = chokidar_1.default.watch(watchRoots, {
|
|
320
|
+
ignoreInitial: true,
|
|
321
|
+
ignored: (pathName) => shouldIgnore(toProjectRelative(pathName), watchConfig),
|
|
322
|
+
awaitWriteFinish: {
|
|
323
|
+
stabilityThreshold: 120,
|
|
324
|
+
pollInterval: 20,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
const triggerRestart = (eventName, changedPath) => {
|
|
328
|
+
const relPath = toProjectRelative(changedPath);
|
|
329
|
+
if (!shouldTrigger(relPath, watchConfig)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (restartTimer) {
|
|
333
|
+
clearTimeout(restartTimer);
|
|
334
|
+
}
|
|
335
|
+
restartTimer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
336
|
+
if (restarting) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
restarting = true;
|
|
340
|
+
console.log(chalk_1.default.yellow(`[${LOG_TAG}] ${eventName}: ${relPath}, restarting...`));
|
|
341
|
+
yield stopGoProcess(goProc);
|
|
342
|
+
goProc = runGoProcess();
|
|
343
|
+
restarting = false;
|
|
344
|
+
}), RESTART_DEBOUNCE_MS);
|
|
345
|
+
};
|
|
346
|
+
watcher
|
|
347
|
+
.on("add", (filePath) => triggerRestart("add", filePath))
|
|
348
|
+
.on("change", (filePath) => triggerRestart("change", filePath))
|
|
349
|
+
.on("unlink", (filePath) => triggerRestart("unlink", filePath))
|
|
350
|
+
.on("error", (error) => {
|
|
351
|
+
console.error(chalk_1.default.red(`[${LOG_TAG}] watcher error: ${String(error)}`));
|
|
352
|
+
});
|
|
353
|
+
const shutdown = () => __awaiter(this, void 0, void 0, function* () {
|
|
354
|
+
if (restartTimer) {
|
|
355
|
+
clearTimeout(restartTimer);
|
|
356
|
+
restartTimer = null;
|
|
357
|
+
}
|
|
358
|
+
yield watcher.close();
|
|
359
|
+
yield stopGoProcess(goProc);
|
|
360
|
+
});
|
|
361
|
+
process.on("SIGINT", () => __awaiter(this, void 0, void 0, function* () {
|
|
362
|
+
yield shutdown();
|
|
363
|
+
process.exit(0);
|
|
364
|
+
}));
|
|
365
|
+
process.on("SIGTERM", () => __awaiter(this, void 0, void 0, function* () {
|
|
366
|
+
yield shutdown();
|
|
367
|
+
process.exit(0);
|
|
368
|
+
}));
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
start().catch((error) => {
|
|
372
|
+
console.error(chalk_1.default.red(`[${LOG_TAG}] failed to start: ${String(error)}`));
|
|
373
|
+
process.exit(1);
|
|
374
|
+
});
|
package/src/develop.js
CHANGED
|
@@ -17,37 +17,134 @@ const concurrently_1 = __importDefault(require("concurrently"));
|
|
|
17
17
|
const fs_extra_1 = require("fs-extra");
|
|
18
18
|
const os_1 = __importDefault(require("os"));
|
|
19
19
|
const path_1 = require("path");
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
20
22
|
const cleanup_1 = __importDefault(require("./cleanup"));
|
|
21
23
|
const postinstall_1 = __importDefault(require("./postinstall"));
|
|
22
24
|
const cwd = process.cwd();
|
|
23
25
|
const serverConfig = (0, fs_extra_1.readJSONSync)((0, path_1.join)(cwd, "server.config.json"));
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function getAirCommand() {
|
|
32
|
-
if (os_1.default.platform() === "darwin") {
|
|
33
|
-
return "~/go/bin/air -c node_modules/nuxt-gin-tools/.air.toml";
|
|
26
|
+
function getGoDevCommand() {
|
|
27
|
+
const scriptPath = (0, path_1.join)(__dirname, "dev-go.js");
|
|
28
|
+
return `"${process.execPath}" "${scriptPath}"`;
|
|
29
|
+
}
|
|
30
|
+
function killPort(port) {
|
|
31
|
+
if (!Number.isInteger(port)) {
|
|
32
|
+
return;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
try {
|
|
35
|
+
const output = (0, child_process_1.execSync)(`lsof -ti tcp:${port}`, {
|
|
36
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
37
|
+
})
|
|
38
|
+
.toString()
|
|
39
|
+
.trim();
|
|
40
|
+
if (!output) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const pids = output
|
|
44
|
+
.split("\n")
|
|
45
|
+
.map((pid) => Number(pid.trim()))
|
|
46
|
+
.filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
47
|
+
for (const pid of pids) {
|
|
48
|
+
try {
|
|
49
|
+
process.kill(pid, "SIGKILL");
|
|
50
|
+
console.log(chalk_1.default.yellow(`Killed process ${pid} on port ${port} (unix)`));
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
// Best-effort: if the process is already gone, ignore.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (_b) {
|
|
58
|
+
// Best-effort: lsof might be missing or no process is listening on the port.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function killPortWindows(port) {
|
|
62
|
+
if (!Number.isInteger(port)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const output = (0, child_process_1.execSync)(`netstat -ano -p tcp`, {
|
|
67
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
68
|
+
})
|
|
69
|
+
.toString()
|
|
70
|
+
.trim();
|
|
71
|
+
if (!output) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const pids = new Set();
|
|
75
|
+
for (const line of output.split("\n")) {
|
|
76
|
+
const trimmed = line.trim();
|
|
77
|
+
if (!trimmed || !trimmed.startsWith("TCP")) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const parts = trimmed.split(/\s+/);
|
|
81
|
+
if (parts.length < 5) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const localAddress = parts[1];
|
|
85
|
+
const pid = Number(parts[parts.length - 1]);
|
|
86
|
+
const match = localAddress.match(/:(\d+)$/);
|
|
87
|
+
if (!match) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const localPort = Number(match[1]);
|
|
91
|
+
if (localPort === port && Number.isInteger(pid) && pid > 0) {
|
|
92
|
+
pids.add(pid);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const pid of pids) {
|
|
96
|
+
try {
|
|
97
|
+
(0, child_process_1.execSync)(`taskkill /PID ${pid} /F`, {
|
|
98
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
99
|
+
});
|
|
100
|
+
console.log(chalk_1.default.yellow(`Killed process ${pid} on port ${port} (win32)`));
|
|
101
|
+
}
|
|
102
|
+
catch (_a) {
|
|
103
|
+
// Best-effort: if the process is already gone, ignore.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (_b) {
|
|
108
|
+
// Best-effort: netstat might be missing or no process is listening on the port.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function killPortsFromConfig() {
|
|
112
|
+
const ports = [serverConfig.ginPort, serverConfig.nuxtPort]
|
|
113
|
+
.filter((port) => Number.isInteger(port) && port > 0)
|
|
114
|
+
.filter((port, index, list) => list.indexOf(port) === index);
|
|
115
|
+
for (const port of ports) {
|
|
116
|
+
if (os_1.default.platform() === "win32") {
|
|
117
|
+
killPortWindows(port);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
killPort(port);
|
|
121
|
+
}
|
|
37
122
|
}
|
|
38
123
|
}
|
|
39
124
|
function develop() {
|
|
40
125
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
126
|
+
const cleanupBeforeDevelop = serverConfig.cleanupBeforeDevelop === true;
|
|
127
|
+
const killPortBeforeDevelop = serverConfig.killPortBeforeDevelop !== false;
|
|
128
|
+
// 如果配置为开发前清理,则直接执行清理和安装后处理
|
|
129
|
+
if (cleanupBeforeDevelop) {
|
|
44
130
|
yield (0, cleanup_1.default)();
|
|
45
131
|
yield (0, postinstall_1.default)();
|
|
46
132
|
}
|
|
133
|
+
else {
|
|
134
|
+
// 否则仅在关键依赖缺失时执行
|
|
135
|
+
if (!(0, fs_extra_1.existsSync)((0, path_1.join)(cwd, "vue/.nuxt")) || !(0, fs_extra_1.existsSync)((0, path_1.join)(cwd, "go.sum"))) {
|
|
136
|
+
yield (0, cleanup_1.default)();
|
|
137
|
+
yield (0, postinstall_1.default)();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 在开发前确保占用端口被释放
|
|
141
|
+
if (killPortBeforeDevelop) {
|
|
142
|
+
killPortsFromConfig();
|
|
143
|
+
}
|
|
47
144
|
(0, fs_extra_1.ensureDirSync)((0, path_1.join)(cwd, ".build/.server"));
|
|
48
145
|
yield (0, concurrently_1.default)([
|
|
49
146
|
{
|
|
50
|
-
command:
|
|
147
|
+
command: getGoDevCommand(),
|
|
51
148
|
name: "go",
|
|
52
149
|
prefixColor: "green",
|
|
53
150
|
},
|
package/src/postinstall.js
CHANGED
|
@@ -9,7 +9,6 @@ const node_child_process_1 = require("node:child_process");
|
|
|
9
9
|
function postInstall() {
|
|
10
10
|
const hasGo = (0, node_child_process_1.spawnSync)("go", ["version"], { stdio: "ignore", shell: true }).status ===
|
|
11
11
|
0;
|
|
12
|
-
const hasAir = (0, node_child_process_1.spawnSync)("air", ["-v"], { stdio: "ignore", shell: true }).status === 0;
|
|
13
12
|
const commands = [
|
|
14
13
|
{
|
|
15
14
|
command: "npx nuxt prepare",
|
|
@@ -27,21 +26,6 @@ function postInstall() {
|
|
|
27
26
|
else {
|
|
28
27
|
console.warn("[nuxt-gin-tools] 未检测到 Go,已跳过 Go 相关安装。请先安装 Go 后再重新运行相关命令。");
|
|
29
28
|
}
|
|
30
|
-
if (!hasAir) {
|
|
31
|
-
const isWindows = process.platform === "win32";
|
|
32
|
-
const pathHint = isWindows
|
|
33
|
-
? [
|
|
34
|
-
"PowerShell: $env:Path += \";$env:USERPROFILE\\go\\bin\"",
|
|
35
|
-
"CMD: set PATH=%PATH%;%USERPROFILE%\\go\\bin",
|
|
36
|
-
].join(" | ")
|
|
37
|
-
: "export PATH=\"$PATH:$HOME/go/bin\"";
|
|
38
|
-
console.warn([
|
|
39
|
-
"[nuxt-gin-tools] 未检测到 air,请先安装 1.63.0 版本并加入 PATH。",
|
|
40
|
-
"安装命令: go install github.com/cosmtrek/air@v1.63.0",
|
|
41
|
-
`PATH 示例: ${pathHint}`,
|
|
42
|
-
"安装完成后请执行: air -v",
|
|
43
|
-
].join("\n"));
|
|
44
|
-
}
|
|
45
29
|
// 执行并发命令
|
|
46
30
|
return (0, concurrently_1.default)(commands).result;
|
|
47
31
|
}
|
package/src/server-config.json
CHANGED
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
"title": "The base url for nuxt. / Nuxt的BaseUrl",
|
|
16
16
|
"type": "string",
|
|
17
17
|
"pattern": "^\\/\\w.+"
|
|
18
|
+
},
|
|
19
|
+
"killPortBeforeDevelop": {
|
|
20
|
+
"title": "Kill port before develop / 开发前释放端口",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": true
|
|
23
|
+
},
|
|
24
|
+
"cleanupBeforeDevelop": {
|
|
25
|
+
"title": "Cleanup before develop / 开发前清理",
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false
|
|
18
28
|
}
|
|
19
29
|
},
|
|
20
30
|
"required": [
|
|
@@ -22,4 +32,4 @@
|
|
|
22
32
|
"nuxtPort",
|
|
23
33
|
"baseUrl"
|
|
24
34
|
]
|
|
25
|
-
}
|
|
35
|
+
}
|
package/.air.toml
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
root = "."
|
|
2
|
-
testdata_dir = "testdata"
|
|
3
|
-
tmp_dir= ".build/.server"
|
|
4
|
-
|
|
5
|
-
[build]
|
|
6
|
-
args_bin = []
|
|
7
|
-
bin = "./.build/.server/development.exe"
|
|
8
|
-
cmd = "go build -o ./.build/.server/development.exe ."
|
|
9
|
-
delay = 0
|
|
10
|
-
exclude_dir = ["assets", ".build/.server", "vendor", "testdata", "node_modules", "vue", "api", ".vscode", ".git"]
|
|
11
|
-
exclude_file = []
|
|
12
|
-
exclude_regex = ["_test.go"]
|
|
13
|
-
exclude_unchanged = false
|
|
14
|
-
follow_symlink = false
|
|
15
|
-
full_bin = ""
|
|
16
|
-
include_dir = []
|
|
17
|
-
include_ext = ["go", "tpl", ".build/.server", "html"]
|
|
18
|
-
include_file = []
|
|
19
|
-
kill_delay = "0s"
|
|
20
|
-
log = "build-errors.log"
|
|
21
|
-
rerun = false
|
|
22
|
-
rerun_delay = 500
|
|
23
|
-
send_interrupt = false
|
|
24
|
-
stop_on_error = false
|
|
25
|
-
|
|
26
|
-
[color]
|
|
27
|
-
app = ""
|
|
28
|
-
build = "yellow"
|
|
29
|
-
main = "magenta"
|
|
30
|
-
runner = "green"
|
|
31
|
-
watcher = "cyan"
|
|
32
|
-
|
|
33
|
-
[log]
|
|
34
|
-
main_only = false
|
|
35
|
-
time = true
|
|
36
|
-
|
|
37
|
-
[misc]
|
|
38
|
-
clean_on_exit = false
|
|
39
|
-
|
|
40
|
-
[screen]
|
|
41
|
-
clear_on_rebuild = false
|
|
42
|
-
keep_scroll = true
|