@rhseung/ps-cli 1.3.3 → 1.4.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/dist/chunk-7MQMPJ3X.js +88 -0
- package/dist/{chunk-PNIGP6LX.js → chunk-7SVCS23X.js} +392 -96
- package/dist/{chunk-2E4VSP6O.js → chunk-NB4OIWND.js} +139 -139
- package/dist/{chunk-EIFFWFLS.js → chunk-OJZLQ6FK.js} +1 -1
- package/dist/chunk-QGMWUOJ3.js +23 -0
- package/dist/commands/config.js +298 -262
- package/dist/commands/fetch.js +262 -254
- package/dist/commands/init.js +138 -101
- package/dist/commands/open.js +74 -78
- package/dist/commands/run.js +99 -98
- package/dist/commands/stats.js +71 -65
- package/dist/commands/submit.js +112 -126
- package/dist/commands/test.js +203 -203
- package/dist/index.js +7 -6
- package/package.json +16 -6
- package/dist/chunk-6ENX5K3C.js +0 -84
- package/dist/chunk-FYS2JH42.js +0 -31
- package/dist/chunk-TQXMB7XV.js +0 -52
package/dist/commands/test.js
CHANGED
|
@@ -1,27 +1,89 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runSolution
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-OJZLQ6FK.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
Command,
|
|
7
|
+
CommandBuilder,
|
|
8
|
+
CommandDef,
|
|
9
|
+
getProblemId,
|
|
10
|
+
resolveLanguage,
|
|
11
|
+
resolveProblemContext
|
|
12
|
+
} from "../chunk-7SVCS23X.js";
|
|
10
13
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "../chunk-6ENX5K3C.js";
|
|
15
|
-
import "../chunk-PNIGP6LX.js";
|
|
16
|
-
import "../chunk-FYS2JH42.js";
|
|
14
|
+
__decorateClass,
|
|
15
|
+
getSupportedLanguagesString
|
|
16
|
+
} from "../chunk-7MQMPJ3X.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/test.tsx
|
|
19
|
-
import { useEffect, useState } from "react";
|
|
20
|
-
import { render, Box as Box2, Text as Text2 } from "ink";
|
|
21
19
|
import { Alert } from "@inkjs/ui";
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
20
|
+
import { Spinner } from "@inkjs/ui";
|
|
21
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
22
|
+
|
|
23
|
+
// src/components/test-result.tsx
|
|
24
|
+
import { Badge, StatusMessage } from "@inkjs/ui";
|
|
25
|
+
import { Box, Text } from "ink";
|
|
26
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
27
|
+
function truncate(text = "", max = 200) {
|
|
28
|
+
return text.length > max ? `${text.slice(0, max)}...` : text;
|
|
29
|
+
}
|
|
30
|
+
function formatDuration(ms) {
|
|
31
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
32
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
33
|
+
}
|
|
34
|
+
function ResultRow({ result }) {
|
|
35
|
+
const badgeColor = result.status === "pass" ? "green" : result.status === "fail" ? "red" : "yellow";
|
|
36
|
+
const statusText = result.status === "pass" ? "PASS" : result.status === "fail" ? "FAIL" : "ERROR";
|
|
37
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
38
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
39
|
+
/* @__PURE__ */ jsx(Badge, { color: badgeColor, children: statusText }),
|
|
40
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
41
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
42
|
+
"\uCF00\uC774\uC2A4 ",
|
|
43
|
+
result.caseId
|
|
44
|
+
] }),
|
|
45
|
+
result.durationMs !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
47
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
48
|
+
"(",
|
|
49
|
+
formatDuration(result.durationMs),
|
|
50
|
+
")"
|
|
51
|
+
] })
|
|
52
|
+
] })
|
|
53
|
+
] }),
|
|
54
|
+
result.status === "fail" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
|
|
55
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
56
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\uAE30\uB300\uAC12:" }),
|
|
57
|
+
/* @__PURE__ */ jsx(Text, { children: truncate(result.expected ?? "") })
|
|
58
|
+
] }),
|
|
59
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
60
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\uC2E4\uC81C\uAC12:" }),
|
|
61
|
+
/* @__PURE__ */ jsx(Text, { children: truncate(result.actual ?? "") })
|
|
62
|
+
] })
|
|
63
|
+
] }),
|
|
64
|
+
result.status === "error" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
|
|
65
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: result.error ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958" }),
|
|
66
|
+
result.stderr && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: truncate(result.stderr) })
|
|
67
|
+
] })
|
|
68
|
+
] });
|
|
69
|
+
}
|
|
70
|
+
function TestResultView({ summary, results }) {
|
|
71
|
+
const allPassed = summary.failed === 0 && summary.errored === 0;
|
|
72
|
+
const summaryVariant = allPassed ? "success" : "error";
|
|
73
|
+
const summaryText = `\uCD1D ${summary.total}\uAC1C | Pass ${summary.passed}${summary.failed > 0 ? ` | Fail ${summary.failed}` : ""}${summary.errored > 0 ? ` | Error ${summary.errored}` : ""}`;
|
|
74
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
75
|
+
/* @__PURE__ */ jsxs(StatusMessage, { variant: summaryVariant, children: [
|
|
76
|
+
"\uD14C\uC2A4\uD2B8 \uACB0\uACFC: ",
|
|
77
|
+
summaryText
|
|
78
|
+
] }),
|
|
79
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: results.map((r) => /* @__PURE__ */ jsx(ResultRow, { result: r }, r.caseId)) })
|
|
80
|
+
] });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/hooks/use-test-runner.ts
|
|
24
84
|
import { join as join2 } from "path";
|
|
85
|
+
import chokidar from "chokidar";
|
|
86
|
+
import { useEffect, useState, useCallback } from "react";
|
|
25
87
|
|
|
26
88
|
// src/services/test-runner.ts
|
|
27
89
|
import { readdir, readFile } from "fs/promises";
|
|
@@ -134,72 +196,8 @@ async function runAllTests({
|
|
|
134
196
|
return { results, summary: buildSummary(results) };
|
|
135
197
|
}
|
|
136
198
|
|
|
137
|
-
// src/
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// src/components/test-result.tsx
|
|
141
|
-
import { Box, Text } from "ink";
|
|
142
|
-
import { Badge, StatusMessage } from "@inkjs/ui";
|
|
143
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
144
|
-
function truncate(text = "", max = 200) {
|
|
145
|
-
return text.length > max ? `${text.slice(0, max)}...` : text;
|
|
146
|
-
}
|
|
147
|
-
function formatDuration(ms) {
|
|
148
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
149
|
-
return `${(ms / 1e3).toFixed(2)}s`;
|
|
150
|
-
}
|
|
151
|
-
function ResultRow({ result }) {
|
|
152
|
-
const badgeColor = result.status === "pass" ? "green" : result.status === "fail" ? "red" : "yellow";
|
|
153
|
-
const statusText = result.status === "pass" ? "PASS" : result.status === "fail" ? "FAIL" : "ERROR";
|
|
154
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
155
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
156
|
-
/* @__PURE__ */ jsx(Badge, { color: badgeColor, children: statusText }),
|
|
157
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
158
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
159
|
-
"\uCF00\uC774\uC2A4 ",
|
|
160
|
-
result.caseId
|
|
161
|
-
] }),
|
|
162
|
-
result.durationMs !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
163
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
164
|
-
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
165
|
-
"(",
|
|
166
|
-
formatDuration(result.durationMs),
|
|
167
|
-
")"
|
|
168
|
-
] })
|
|
169
|
-
] })
|
|
170
|
-
] }),
|
|
171
|
-
result.status === "fail" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
|
|
172
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
173
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\uAE30\uB300\uAC12:" }),
|
|
174
|
-
/* @__PURE__ */ jsx(Text, { children: truncate(result.expected ?? "") })
|
|
175
|
-
] }),
|
|
176
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
177
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\uC2E4\uC81C\uAC12:" }),
|
|
178
|
-
/* @__PURE__ */ jsx(Text, { children: truncate(result.actual ?? "") })
|
|
179
|
-
] })
|
|
180
|
-
] }),
|
|
181
|
-
result.status === "error" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
|
|
182
|
-
/* @__PURE__ */ jsx(Text, { color: "yellow", children: result.error ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958" }),
|
|
183
|
-
result.stderr && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: truncate(result.stderr) })
|
|
184
|
-
] })
|
|
185
|
-
] });
|
|
186
|
-
}
|
|
187
|
-
function TestResultView({ summary, results }) {
|
|
188
|
-
const allPassed = summary.failed === 0 && summary.errored === 0;
|
|
189
|
-
const summaryVariant = allPassed ? "success" : "error";
|
|
190
|
-
const summaryText = `\uCD1D ${summary.total}\uAC1C | Pass ${summary.passed}${summary.failed > 0 ? ` | Fail ${summary.failed}` : ""}${summary.errored > 0 ? ` | Error ${summary.errored}` : ""}`;
|
|
191
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
192
|
-
/* @__PURE__ */ jsxs(StatusMessage, { variant: summaryVariant, children: [
|
|
193
|
-
"\uD14C\uC2A4\uD2B8 \uACB0\uACFC: ",
|
|
194
|
-
summaryText
|
|
195
|
-
] }),
|
|
196
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: results.map((r) => /* @__PURE__ */ jsx(ResultRow, { result: r }, r.caseId)) })
|
|
197
|
-
] });
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// src/commands/test.tsx
|
|
201
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
202
|
-
function TestCommand({
|
|
199
|
+
// src/hooks/use-test-runner.ts
|
|
200
|
+
function useTestRunner({
|
|
203
201
|
problemDir,
|
|
204
202
|
language,
|
|
205
203
|
watch,
|
|
@@ -215,46 +213,54 @@ function TestCommand({
|
|
|
215
213
|
errored: 0
|
|
216
214
|
});
|
|
217
215
|
const [error, setError] = useState(null);
|
|
218
|
-
const runTests = (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
};
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
void runTests();
|
|
238
|
-
if (watch) {
|
|
239
|
-
const watcher = chokidar.watch(
|
|
240
|
-
[
|
|
241
|
-
join2(problemDir, "solution.*"),
|
|
242
|
-
join2(problemDir, "input*.txt"),
|
|
243
|
-
join2(problemDir, "output*.txt")
|
|
244
|
-
],
|
|
245
|
-
{
|
|
246
|
-
ignoreInitial: true
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
watcher.on("change", () => {
|
|
250
|
-
runTests(true);
|
|
216
|
+
const runTests = useCallback(
|
|
217
|
+
(isWatchTrigger = false) => {
|
|
218
|
+
if (isWatchTrigger && watch) {
|
|
219
|
+
console.clear();
|
|
220
|
+
}
|
|
221
|
+
setStatus("loading");
|
|
222
|
+
void runAllTests({
|
|
223
|
+
problemDir,
|
|
224
|
+
language,
|
|
225
|
+
timeoutMs
|
|
226
|
+
}).then(({ results: results2, summary: summary2 }) => {
|
|
227
|
+
setResults(results2);
|
|
228
|
+
setSummary(summary2);
|
|
229
|
+
setStatus("ready");
|
|
230
|
+
}).catch((err) => {
|
|
231
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
232
|
+
setStatus("error");
|
|
251
233
|
});
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
234
|
+
},
|
|
235
|
+
[problemDir, language, timeoutMs, watch]
|
|
236
|
+
);
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const timer = setTimeout(() => {
|
|
239
|
+
void runTests();
|
|
240
|
+
}, 0);
|
|
241
|
+
return () => clearTimeout(timer);
|
|
242
|
+
}, [runTests]);
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
if (!watch) {
|
|
245
|
+
return void 0;
|
|
255
246
|
}
|
|
256
|
-
|
|
257
|
-
|
|
247
|
+
const watcher = chokidar.watch(
|
|
248
|
+
[
|
|
249
|
+
join2(problemDir, "solution.*"),
|
|
250
|
+
join2(problemDir, "input*.txt"),
|
|
251
|
+
join2(problemDir, "output*.txt")
|
|
252
|
+
],
|
|
253
|
+
{
|
|
254
|
+
ignoreInitial: true
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
watcher.on("change", () => {
|
|
258
|
+
runTests(true);
|
|
259
|
+
});
|
|
260
|
+
return () => {
|
|
261
|
+
void watcher.close();
|
|
262
|
+
};
|
|
263
|
+
}, [problemDir, watch, runTests]);
|
|
258
264
|
useEffect(() => {
|
|
259
265
|
if (!watch && status === "ready") {
|
|
260
266
|
const timer = setTimeout(() => onComplete(), 200);
|
|
@@ -262,6 +268,30 @@ function TestCommand({
|
|
|
262
268
|
}
|
|
263
269
|
return void 0;
|
|
264
270
|
}, [status, watch, onComplete]);
|
|
271
|
+
return {
|
|
272
|
+
status,
|
|
273
|
+
results,
|
|
274
|
+
summary,
|
|
275
|
+
error
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/commands/test.tsx
|
|
280
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
281
|
+
function TestView({
|
|
282
|
+
problemDir,
|
|
283
|
+
language,
|
|
284
|
+
watch,
|
|
285
|
+
timeoutMs,
|
|
286
|
+
onComplete
|
|
287
|
+
}) {
|
|
288
|
+
const { status, results, summary, error } = useTestRunner({
|
|
289
|
+
problemDir,
|
|
290
|
+
language,
|
|
291
|
+
watch,
|
|
292
|
+
timeoutMs,
|
|
293
|
+
onComplete
|
|
294
|
+
});
|
|
265
295
|
if (status === "loading") {
|
|
266
296
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsx2(Spinner, { label: "\uD14C\uC2A4\uD2B8 \uC2E4\uD589 \uC911..." }) });
|
|
267
297
|
}
|
|
@@ -285,93 +315,63 @@ function TestCommand({
|
|
|
285
315
|
/* @__PURE__ */ jsx2(TestResultView, { results, summary })
|
|
286
316
|
] });
|
|
287
317
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
const lang = detectLanguageFromFile(solutionFile);
|
|
295
|
-
if (!lang) {
|
|
296
|
-
throw new Error(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5B8\uC5B4\uC785\uB2C8\uB2E4: ${solutionFile}`);
|
|
297
|
-
}
|
|
298
|
-
return lang;
|
|
299
|
-
}
|
|
300
|
-
async function testCommand(options = {}) {
|
|
301
|
-
const currentPathProblemId = detectProblemIdFromPath(process.cwd());
|
|
302
|
-
const problemDir = options.id && currentPathProblemId !== options.id ? getProblemDirPath(options.id) : process.cwd();
|
|
303
|
-
const language = options.language ?? await detectLanguage(problemDir);
|
|
304
|
-
return new Promise((resolve) => {
|
|
305
|
-
const { unmount } = render(
|
|
306
|
-
/* @__PURE__ */ jsx2(
|
|
307
|
-
TestCommand,
|
|
308
|
-
{
|
|
309
|
-
problemDir,
|
|
310
|
-
language,
|
|
311
|
-
watch: Boolean(options.watch),
|
|
312
|
-
timeoutMs: options.timeoutMs,
|
|
313
|
-
onComplete: () => {
|
|
314
|
-
unmount();
|
|
315
|
-
resolve();
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
)
|
|
318
|
+
var TestCommand = class extends Command {
|
|
319
|
+
async execute(args, flags) {
|
|
320
|
+
const problemId = getProblemId(args);
|
|
321
|
+
const context = await resolveProblemContext(
|
|
322
|
+
problemId !== null && problemId !== void 0 ? [problemId.toString()] : []
|
|
319
323
|
);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
\uC0AC\uC6A9\uBC95:
|
|
324
|
-
$ ps test [\uBB38\uC81C\uBC88\uD638] [\uC635\uC158]
|
|
325
|
-
|
|
326
|
-
\uC124\uBA85:
|
|
327
|
-
\uC608\uC81C \uC785\uCD9C\uB825 \uAE30\uBC18\uC73C\uB85C \uB85C\uCEEC \uD14C\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4.
|
|
328
|
-
- \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uD14C\uC2A4\uD2B8 \uC2E4\uD589
|
|
329
|
-
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
330
|
-
- input*.txt\uC640 output*.txt \uD30C\uC77C\uC744 \uAE30\uBC18\uC73C\uB85C \uD14C\uC2A4\uD2B8
|
|
331
|
-
- \uBB38\uC81C\uC758 \uC2DC\uAC04 \uC81C\uD55C\uC744 \uC790\uB3D9\uC73C\uB85C \uC801\uC6A9
|
|
332
|
-
- --watch \uC635\uC158\uC73C\uB85C \uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8
|
|
333
|
-
|
|
334
|
-
\uC635\uC158:
|
|
335
|
-
--language, -l \uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
336
|
-
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}
|
|
337
|
-
--watch, -w watch \uBAA8\uB4DC (\uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8)
|
|
338
|
-
solution.*, input*.txt, output*.txt \uD30C\uC77C \uBCC0\uACBD \uAC10\uC9C0
|
|
339
|
-
|
|
340
|
-
\uC608\uC81C:
|
|
341
|
-
$ ps test # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uD14C\uC2A4\uD2B8
|
|
342
|
-
$ ps test 1000 # 1000\uBC88 \uBB38\uC81C \uD14C\uC2A4\uD2B8
|
|
343
|
-
$ ps test --watch # watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8
|
|
344
|
-
$ ps test 1000 --watch # 1000\uBC88 \uBB38\uC81C\uB97C watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8
|
|
345
|
-
`;
|
|
346
|
-
async function testExecute(args, flags) {
|
|
347
|
-
if (flags.help) {
|
|
348
|
-
console.log(testHelp.trim());
|
|
349
|
-
process.exit(0);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
const problemId = getProblemId(args);
|
|
353
|
-
const validLanguages = getSupportedLanguages();
|
|
354
|
-
const language = flags.language;
|
|
355
|
-
if (flags.language && language && !validLanguages.includes(language)) {
|
|
356
|
-
console.error(
|
|
357
|
-
`\uC624\uB958: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5B8\uC5B4\uC785\uB2C8\uB2E4. (${getSupportedLanguagesString()})`
|
|
324
|
+
const language = await resolveLanguage(
|
|
325
|
+
context.problemDir,
|
|
326
|
+
flags.language
|
|
358
327
|
);
|
|
359
|
-
|
|
328
|
+
await this.renderView(TestView, {
|
|
329
|
+
problemDir: context.problemDir,
|
|
330
|
+
language,
|
|
331
|
+
watch: Boolean(flags.watch),
|
|
332
|
+
timeoutMs: flags.timeoutMs
|
|
333
|
+
});
|
|
360
334
|
}
|
|
361
|
-
await testCommand({
|
|
362
|
-
id: problemId ?? void 0,
|
|
363
|
-
language: language ?? void 0,
|
|
364
|
-
watch: Boolean(flags.watch)
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
var testCommandDef = {
|
|
368
|
-
name: "test",
|
|
369
|
-
help: testHelp,
|
|
370
|
-
execute: testExecute
|
|
371
335
|
};
|
|
372
|
-
|
|
336
|
+
TestCommand = __decorateClass([
|
|
337
|
+
CommandDef({
|
|
338
|
+
name: "test",
|
|
339
|
+
description: `\uC608\uC81C \uC785\uCD9C\uB825 \uAE30\uBC18\uC73C\uB85C \uB85C\uCEEC \uD14C\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4.
|
|
340
|
+
- \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uD14C\uC2A4\uD2B8 \uC2E4\uD589
|
|
341
|
+
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
342
|
+
- input*.txt\uC640 output*.txt \uD30C\uC77C\uC744 \uAE30\uBC18\uC73C\uB85C \uD14C\uC2A4\uD2B8
|
|
343
|
+
- \uBB38\uC81C\uC758 \uC2DC\uAC04 \uC81C\uD55C\uC744 \uC790\uB3D9\uC73C\uB85C \uC801\uC6A9
|
|
344
|
+
- --watch \uC635\uC158\uC73C\uB85C \uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8`,
|
|
345
|
+
flags: [
|
|
346
|
+
{
|
|
347
|
+
name: "language",
|
|
348
|
+
options: {
|
|
349
|
+
shortFlag: "l",
|
|
350
|
+
description: `\uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
351
|
+
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}`
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: "watch",
|
|
356
|
+
options: {
|
|
357
|
+
shortFlag: "w",
|
|
358
|
+
description: `watch \uBAA8\uB4DC (\uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8)
|
|
359
|
+
solution.*, input*.txt, output*.txt \uD30C\uC77C \uBCC0\uACBD \uAC10\uC9C0`
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
],
|
|
363
|
+
autoDetectProblemId: true,
|
|
364
|
+
autoDetectLanguage: true,
|
|
365
|
+
examples: [
|
|
366
|
+
"test # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uD14C\uC2A4\uD2B8",
|
|
367
|
+
"test 1000 # 1000\uBC88 \uBB38\uC81C \uD14C\uC2A4\uD2B8",
|
|
368
|
+
"test --watch # watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8",
|
|
369
|
+
"test 1000 --watch # 1000\uBC88 \uBB38\uC81C\uB97C watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8"
|
|
370
|
+
]
|
|
371
|
+
})
|
|
372
|
+
], TestCommand);
|
|
373
|
+
var test_default = CommandBuilder.fromClass(TestCommand);
|
|
373
374
|
export {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
testHelp
|
|
375
|
+
TestCommand,
|
|
376
|
+
test_default as default
|
|
377
377
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getSupportedLanguagesString
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
|
|
4
|
+
} from "./chunk-7MQMPJ3X.js";
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { readdir } from "fs/promises";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6
11
|
|
|
7
12
|
// node_modules/meow/build/index.js
|
|
8
13
|
import process4 from "process";
|
|
@@ -9356,10 +9361,6 @@ var meow = (helpText, options = {}) => {
|
|
|
9356
9361
|
};
|
|
9357
9362
|
|
|
9358
9363
|
// src/index.ts
|
|
9359
|
-
import { readdir } from "fs/promises";
|
|
9360
|
-
import { join, dirname } from "path";
|
|
9361
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9362
|
-
import { existsSync } from "fs";
|
|
9363
9364
|
function getCommandsDir() {
|
|
9364
9365
|
const __filename = fileURLToPath3(import.meta.url);
|
|
9365
9366
|
const __dirname = dirname(__filename);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhseung/ps-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "백준(BOJ) 문제 해결을 위한 통합 CLI 도구",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
"build": "tsup && node scripts/add-shebang.js",
|
|
15
15
|
"dev": "tsup --watch",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
17
|
+
"lint": "eslint",
|
|
18
|
+
"format": "prettier",
|
|
19
|
+
"check": "prettier --write . && eslint --fix",
|
|
20
|
+
"patch": "npm version patch && bun publish --access=public",
|
|
21
|
+
"minor": "npm version minor && bun publish --access=public",
|
|
22
|
+
"major": "npm version major && bun publish --access=public"
|
|
20
23
|
},
|
|
21
24
|
"keywords": [
|
|
22
25
|
"boj",
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
"url": "https://github.com/rhseung/ps-cli/issues"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
42
|
+
"@inkjs/ui": "^2.0.0",
|
|
39
43
|
"@types/gradient-string": "^1.1.6",
|
|
40
44
|
"chalk": "^5.3.0",
|
|
41
45
|
"cheerio": "^1.0.0",
|
|
@@ -44,7 +48,6 @@
|
|
|
44
48
|
"execa": "^9.4.0",
|
|
45
49
|
"gradient-string": "^2.0.2",
|
|
46
50
|
"ink": "^5.0.1",
|
|
47
|
-
"@inkjs/ui": "^2.0.0",
|
|
48
51
|
"meow": "^14.0.0",
|
|
49
52
|
"react": "^18.3.1"
|
|
50
53
|
},
|
|
@@ -52,7 +55,14 @@
|
|
|
52
55
|
"@types/node": "^22.10.2",
|
|
53
56
|
"@types/react": "^18.3.12",
|
|
54
57
|
"tsup": "^8.3.5",
|
|
55
|
-
"typescript": "^5.7.2"
|
|
58
|
+
"typescript": "^5.7.2",
|
|
59
|
+
"typescript-eslint": "^8.50.1",
|
|
60
|
+
"eslint-config-prettier": "^10.1.8",
|
|
61
|
+
"eslint-plugin-import": "^2.32.0",
|
|
62
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
63
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
64
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
65
|
+
"globals": "^16.5.0"
|
|
56
66
|
},
|
|
57
67
|
"engines": {
|
|
58
68
|
"node": ">=18.0.0"
|
package/dist/chunk-6ENX5K3C.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getProblemDir
|
|
4
|
-
} from "./chunk-PNIGP6LX.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/problem-id.ts
|
|
7
|
-
import { join, dirname } from "path";
|
|
8
|
-
import { existsSync } from "fs";
|
|
9
|
-
function detectProblemIdFromPath(cwd = process.cwd()) {
|
|
10
|
-
const problemDir = getProblemDir();
|
|
11
|
-
const normalizedPath = cwd.replace(/\\/g, "/");
|
|
12
|
-
if (problemDir === "." || problemDir === "") {
|
|
13
|
-
const segments = normalizedPath.split("/").filter(Boolean);
|
|
14
|
-
const lastSegment = segments[segments.length - 1];
|
|
15
|
-
if (lastSegment) {
|
|
16
|
-
const problemId2 = parseInt(lastSegment, 10);
|
|
17
|
-
if (!isNaN(problemId2) && problemId2 > 0 && lastSegment === problemId2.toString()) {
|
|
18
|
-
return problemId2;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const dirPattern = `/${problemDir}/`;
|
|
24
|
-
const dirIndex = normalizedPath.indexOf(dirPattern);
|
|
25
|
-
if (dirIndex === -1) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const afterDir = normalizedPath.substring(dirIndex + dirPattern.length);
|
|
29
|
-
if (!afterDir) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const firstSegment = afterDir.split("/")[0];
|
|
33
|
-
if (!firstSegment) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
const problemId = parseInt(firstSegment, 10);
|
|
37
|
-
if (isNaN(problemId) || problemId <= 0) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
if (firstSegment !== problemId.toString()) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
return problemId;
|
|
44
|
-
}
|
|
45
|
-
function getProblemId(args, cwd = process.cwd()) {
|
|
46
|
-
if (args.length > 0 && args[0]) {
|
|
47
|
-
const problemId = parseInt(args[0], 10);
|
|
48
|
-
if (!isNaN(problemId) && problemId > 0) {
|
|
49
|
-
return problemId;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return detectProblemIdFromPath(cwd);
|
|
53
|
-
}
|
|
54
|
-
function findProjectRoot(startDir = process.cwd()) {
|
|
55
|
-
let currentDir = startDir;
|
|
56
|
-
const rootPath = process.platform === "win32" ? currentDir.split("\\")[0] + "\\" : "/";
|
|
57
|
-
while (currentDir !== rootPath) {
|
|
58
|
-
const projectConfigPath = join(currentDir, ".ps-cli.json");
|
|
59
|
-
if (existsSync(projectConfigPath)) {
|
|
60
|
-
return currentDir;
|
|
61
|
-
}
|
|
62
|
-
const parentDir = dirname(currentDir);
|
|
63
|
-
if (parentDir === currentDir) {
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
currentDir = parentDir;
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
function getProblemDirPath(problemId, cwd = process.cwd()) {
|
|
71
|
-
const problemDir = getProblemDir();
|
|
72
|
-
const projectRoot = findProjectRoot(cwd);
|
|
73
|
-
const baseDir = projectRoot || cwd;
|
|
74
|
-
if (problemDir === "." || problemDir === "") {
|
|
75
|
-
return join(baseDir, problemId.toString());
|
|
76
|
-
}
|
|
77
|
-
return join(baseDir, problemDir, problemId.toString());
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
detectProblemIdFromPath,
|
|
82
|
-
getProblemId,
|
|
83
|
-
getProblemDirPath
|
|
84
|
-
};
|
package/dist/chunk-FYS2JH42.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
__commonJS,
|
|
30
|
-
__toESM
|
|
31
|
-
};
|