@rhseung/ps-cli 1.3.3 → 1.5.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.
@@ -1,27 +1,89 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSolution
4
- } from "../chunk-EIFFWFLS.js";
4
+ } from "../chunk-OJZLQ6FK.js";
5
5
  import {
6
- detectLanguageFromFile,
7
- getSupportedLanguages,
8
- getSupportedLanguagesString
9
- } from "../chunk-TQXMB7XV.js";
6
+ Command,
7
+ CommandBuilder,
8
+ CommandDef,
9
+ getProblemId,
10
+ resolveLanguage,
11
+ resolveProblemContext
12
+ } from "../chunk-7SVCS23X.js";
10
13
  import {
11
- detectProblemIdFromPath,
12
- getProblemDirPath,
13
- getProblemId
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 chokidar from "chokidar";
23
- import { readdir as readdir2 } from "fs/promises";
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/commands/test.tsx
138
- import { Spinner } from "@inkjs/ui";
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 = (isWatchTrigger = false) => {
219
- if (isWatchTrigger && watch) {
220
- console.clear();
221
- }
222
- setStatus("loading");
223
- void runAllTests({
224
- problemDir,
225
- language,
226
- timeoutMs
227
- }).then(({ results: results2, summary: summary2 }) => {
228
- setResults(results2);
229
- setSummary(summary2);
230
- setStatus("ready");
231
- }).catch((err) => {
232
- setError(err instanceof Error ? err.message : String(err));
233
- setStatus("error");
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
- return () => {
253
- void watcher.close();
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
- return void 0;
257
- }, [problemDir, language, watch]);
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
- async function detectLanguage(problemDir) {
289
- const files = await readdir2(problemDir);
290
- const solutionFile = files.find((f) => f.startsWith("solution."));
291
- if (!solutionFile) {
292
- throw new Error("solution.* \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
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
- var testHelp = `
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
- process.exit(1);
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
- var test_default = testCommandDef;
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
- test_default as default,
375
- testExecute,
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-TQXMB7XV.js";
5
- import "./chunk-FYS2JH42.js";
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.3",
3
+ "version": "1.5.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
- "release:patch": "npm version patch && bun publish --access=public",
18
- "release:minor": "npm version minor && bun publish --access=public",
19
- "release:major": "npm version major && bun publish --access=public"
17
+ "lint": "eslint",
18
+ "format": "prettier --check .",
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"
@@ -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
- };
@@ -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
- };