@rhseung/ps-cli 1.7.3 → 1.7.5

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,22 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSolution
4
- } from "../chunk-OJZLQ6FK.js";
4
+ } from "../chunk-VIHXBCOZ.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
8
8
  CommandDef,
9
- getProblemId,
10
9
  resolveLanguage,
11
10
  resolveProblemContext
12
- } from "../chunk-TNGUME4H.js";
11
+ } from "../chunk-F4LZ6ENP.js";
13
12
  import {
14
13
  __decorateClass,
15
14
  getSupportedLanguagesString
16
15
  } from "../chunk-7MQMPJ3X.js";
17
16
 
18
17
  // src/commands/run.tsx
19
- import { readdir } from "fs/promises";
20
18
  import { join } from "path";
21
19
  import { StatusMessage, Alert } from "@inkjs/ui";
22
20
  import { Spinner } from "@inkjs/ui";
@@ -78,6 +76,17 @@ function RunView({
78
76
  onComplete
79
77
  });
80
78
  if (status === "loading") {
79
+ if (!inputFile) {
80
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
81
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uD45C\uC900 \uC785\uB825 \uB300\uAE30 \uC911" }) }),
82
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
83
+ /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "\uC785\uB825\uC744 \uC785\uB825\uD55C \uD6C4, \uB9C8\uC9C0\uB9C9 \uC904\uC5D0\uC11C Enter\uB97C \uB204\uB974\uACE0:" }),
84
+ /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "\u2022 macOS/Linux: Ctrl+D" }),
85
+ /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "\u2022 Windows: Ctrl+Z (\uADF8\uB9AC\uACE0 Enter)" }),
86
+ /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: '\uC608: "3 4" \uC785\uB825 \u2192 Enter \u2192 Ctrl+D' })
87
+ ] })
88
+ ] });
89
+ }
81
90
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Spinner, { label: "\uCF54\uB4DC \uC2E4\uD589 \uC911..." }) });
82
91
  }
83
92
  if (status === "error") {
@@ -88,7 +97,7 @@ function RunView({
88
97
  }
89
98
  if (result) {
90
99
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
91
- /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
100
+ /* @__PURE__ */ jsxs(Box, { children: [
92
101
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uC2E4\uD589 \uACB0\uACFC" }),
93
102
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
94
103
  problemDir,
@@ -103,9 +112,13 @@ function RunView({
103
112
  result.exitCode,
104
113
  ")"
105
114
  ] }) : /* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: "\uC2E4\uD589 \uC644\uB8CC" }),
115
+ result.input && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
116
+ /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "\uC785\uB825:" }),
117
+ /* @__PURE__ */ jsx(Text, { children: result.input.trim() })
118
+ ] }),
106
119
  result.stdout && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
107
120
  /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "\uCD9C\uB825:" }),
108
- /* @__PURE__ */ jsx(Text, { children: result.stdout })
121
+ /* @__PURE__ */ jsx(Text, { children: result.stdout.trim() })
109
122
  ] }),
110
123
  result.stderr && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
111
124
  /* @__PURE__ */ jsx(Text, { color: "yellow", dimColor: true, children: "\uC5D0\uB7EC \uCD9C\uB825:" }),
@@ -123,30 +136,31 @@ function RunView({
123
136
  }
124
137
  var RunCommand = class extends Command {
125
138
  async execute(args, flags) {
126
- const problemId = getProblemId(args);
127
- const context = await resolveProblemContext(
128
- problemId !== null ? [problemId.toString()] : []
129
- );
130
- const inputPath = flags.input ? join(context.problemDir, flags.input) : await this.findInputFile(context.problemDir);
139
+ const context = await resolveProblemContext(args);
140
+ let inputPath;
141
+ if (flags.input) {
142
+ const inputValue = flags.input;
143
+ if (/^\d+$/.test(inputValue)) {
144
+ inputPath = join(
145
+ context.archiveDir,
146
+ "testcases",
147
+ inputValue,
148
+ "input.txt"
149
+ );
150
+ } else {
151
+ inputPath = join(context.archiveDir, inputValue);
152
+ }
153
+ }
131
154
  const detectedLanguage = await resolveLanguage(
132
- context.problemDir,
155
+ context.archiveDir,
133
156
  flags.language
134
157
  );
135
158
  await this.renderView(RunView, {
136
- problemDir: context.problemDir,
159
+ problemDir: context.archiveDir,
137
160
  language: detectedLanguage,
138
161
  inputFile: inputPath
139
162
  });
140
163
  }
141
- // 입력 파일 찾기: private 메서드
142
- async findInputFile(problemDir) {
143
- const files = await readdir(problemDir);
144
- const inputFile = files.find((f) => f === "input1.txt") || files.find((f) => f === "input.txt");
145
- if (!inputFile) {
146
- throw new Error("input.txt \uB610\uB294 input1.txt \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
147
- }
148
- return join(problemDir, inputFile);
149
- }
150
164
  };
151
165
  RunCommand = __decorateClass([
152
166
  CommandDef({
@@ -154,7 +168,8 @@ RunCommand = __decorateClass([
154
168
  description: `\uCF54\uB4DC\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4 (\uD14C\uC2A4\uD2B8 \uC5C6\uC774).
155
169
  - \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uCF54\uB4DC \uC2E4\uD589
156
170
  - solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
157
- - input.txt \uB610\uB294 input1.txt\uB97C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC0AC\uC6A9
171
+ - --input \uC635\uC158\uC73C\uB85C \uC785\uB825 \uD30C\uC77C \uC9C0\uC815 \uAC00\uB2A5 (\uC608: testcases/1/input.txt)
172
+ - \uC635\uC158 \uC5C6\uC774 \uC2E4\uD589 \uC2DC \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC785\uB825 \uBC1B\uAE30
158
173
  - \uD14C\uC2A4\uD2B8 \uCF00\uC774\uC2A4 \uAC80\uC99D \uC5C6\uC774 \uB2E8\uC21C \uC2E4\uD589`,
159
174
  flags: [
160
175
  {
@@ -169,17 +184,18 @@ RunCommand = __decorateClass([
169
184
  name: "input",
170
185
  options: {
171
186
  shortFlag: "i",
172
- description: "\uC785\uB825 \uD30C\uC77C \uC9C0\uC815 (\uAE30\uBCF8\uAC12: input.txt \uB610\uB294 input1.txt)"
187
+ description: "\uC785\uB825 \uD30C\uC77C \uC9C0\uC815 (\uC608: 1 \uB610\uB294 testcases/1/input.txt)"
173
188
  }
174
189
  }
175
190
  ],
176
191
  autoDetectProblemId: true,
177
192
  autoDetectLanguage: true,
178
193
  examples: [
179
- "run # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589",
180
- "run 1000 # 1000\uBC88 \uBB38\uC81C \uC2E4\uD589",
181
- "run --language python # Python\uC73C\uB85C \uC2E4\uD589",
182
- "run --input input2.txt # \uD2B9\uC815 \uC785\uB825 \uD30C\uC77C \uC0AC\uC6A9"
194
+ "run # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC2E4\uD589",
195
+ "run 1000 # 1000\uBC88 \uBB38\uC81C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC2E4\uD589",
196
+ "run --language python # Python\uC73C\uB85C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC2E4\uD589",
197
+ "run --input 1 # \uD14C\uC2A4\uD2B8 \uCF00\uC774\uC2A4 1\uBC88 \uC0AC\uC6A9",
198
+ "run --input testcases/1/input.txt # \uC804\uCCB4 \uACBD\uB85C\uB85C \uC785\uB825 \uD30C\uC77C \uC9C0\uC815"
183
199
  ]
184
200
  })
185
201
  ], RunCommand);
@@ -16,10 +16,10 @@ import {
16
16
  Command,
17
17
  CommandBuilder,
18
18
  CommandDef,
19
- getProblemDirPath,
19
+ getArchiveDirPath,
20
20
  getTierColor,
21
21
  getTierName
22
- } from "../chunk-TNGUME4H.js";
22
+ } from "../chunk-F4LZ6ENP.js";
23
23
  import {
24
24
  __decorateClass
25
25
  } from "../chunk-7MQMPJ3X.js";
@@ -270,7 +270,7 @@ function WorkbookSearchView({
270
270
  ] });
271
271
  }
272
272
  const problemsWithSolvedStatus = problems.map((problem) => {
273
- const problemDirPath = getProblemDirPath(problem.problemId);
273
+ const problemDirPath = getArchiveDirPath(problem.problemId);
274
274
  const isSolved = existsSync(problemDirPath);
275
275
  return {
276
276
  problemId: problem.problemId,
@@ -362,7 +362,7 @@ function SearchView({ query, onComplete }) {
362
362
  const searchResults = await searchProblems(query, currentPage);
363
363
  const resultsWithSolvedStatus = searchResults.problems.map(
364
364
  (problem) => {
365
- const problemDirPath = getProblemDirPath(problem.problemId);
365
+ const problemDirPath = getArchiveDirPath(problem.problemId);
366
366
  const isSolved = existsSync(problemDirPath);
367
367
  return {
368
368
  ...problem,
@@ -14,7 +14,7 @@ import {
14
14
  getSolvedAcHandle,
15
15
  getTierColor,
16
16
  getTierName
17
- } from "../chunk-TNGUME4H.js";
17
+ } from "../chunk-F4LZ6ENP.js";
18
18
  import {
19
19
  __decorateClass
20
20
  } from "../chunk-7MQMPJ3X.js";
@@ -8,10 +8,9 @@ import {
8
8
  CommandDef,
9
9
  detectProblemIdFromPath,
10
10
  findSolutionFile,
11
- getProblemId,
12
11
  resolveLanguage,
13
12
  resolveProblemContext
14
- } from "../chunk-TNGUME4H.js";
13
+ } from "../chunk-F4LZ6ENP.js";
15
14
  import {
16
15
  __decorateClass,
17
16
  getSupportedLanguagesString
@@ -195,19 +194,15 @@ function SubmitView({
195
194
  }
196
195
  var SubmitCommand = class extends Command {
197
196
  async execute(args, flags) {
198
- const problemId = getProblemId(args);
199
- const context = await resolveProblemContext(
200
- problemId !== null ? [problemId.toString()] : [],
201
- { requireId: true }
202
- );
203
- const sourcePath = await findSolutionFile(context.problemDir);
197
+ const context = await resolveProblemContext(args, { requireId: true });
198
+ const sourcePath = await findSolutionFile(context.archiveDir);
204
199
  const detectedLanguage = await resolveLanguage(
205
- context.problemDir,
200
+ context.archiveDir,
206
201
  flags.language
207
202
  );
208
203
  let finalProblemId = context.problemId;
209
204
  if (finalProblemId === null) {
210
- finalProblemId = detectProblemIdFromPath(context.problemDir);
205
+ finalProblemId = detectProblemIdFromPath(context.archiveDir);
211
206
  }
212
207
  if (finalProblemId === null) {
213
208
  throw new Error(
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSolution
4
- } from "../chunk-OJZLQ6FK.js";
4
+ } from "../chunk-VIHXBCOZ.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
8
8
  CommandDef,
9
- getProblemId,
10
9
  resolveLanguage,
11
10
  resolveProblemContext
12
- } from "../chunk-TNGUME4H.js";
11
+ } from "../chunk-F4LZ6ENP.js";
13
12
  import {
14
13
  __decorateClass,
15
14
  getSupportedLanguagesString
@@ -123,8 +122,14 @@ async function runAllTests({
123
122
  language,
124
123
  timeoutMs
125
124
  }) {
126
- const entries = await readdir(problemDir);
127
- const inputFiles = entries.filter((f) => /^input\d+\.txt$/.test(f));
125
+ const testcasesDir = join(problemDir, "testcases");
126
+ let caseDirs = [];
127
+ try {
128
+ const entries = await readdir(testcasesDir);
129
+ caseDirs = entries.filter((entry) => /^\d+$/.test(entry)).sort((a, b) => Number(a) - Number(b)).map((entry) => join(testcasesDir, entry));
130
+ } catch {
131
+ return { results: [], summary: buildSummary([]) };
132
+ }
128
133
  const results = [];
129
134
  let effectiveTimeout = timeoutMs;
130
135
  if (effectiveTimeout == null) {
@@ -148,11 +153,10 @@ async function runAllTests({
148
153
  if (effectiveTimeout == null) {
149
154
  effectiveTimeout = 5e3;
150
155
  }
151
- for (const inputFile of inputFiles) {
152
- const match = inputFile.match(/input(\d+)\.txt$/);
153
- const caseId = match ? Number(match[1]) : results.length + 1;
154
- const inputPath = join(problemDir, inputFile);
155
- const outputPath = join(problemDir, `output${caseId}.txt`);
156
+ for (const caseDir of caseDirs) {
157
+ const caseId = Number(join(caseDir).split("/").pop() || "0");
158
+ const inputPath = join(caseDir, "input.txt");
159
+ const outputPath = join(caseDir, "output.txt");
156
160
  let expected;
157
161
  try {
158
162
  expected = await readFile(outputPath, "utf-8");
@@ -161,7 +165,7 @@ async function runAllTests({
161
165
  caseId,
162
166
  inputPath,
163
167
  status: "error",
164
- error: "\uAE30\uB300 \uCD9C\uB825(output*.txt)\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
168
+ error: "\uAE30\uB300 \uCD9C\uB825(output.txt)\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
165
169
  });
166
170
  continue;
167
171
  }
@@ -247,8 +251,7 @@ function useTestRunner({
247
251
  const watcher = chokidar.watch(
248
252
  [
249
253
  join2(problemDir, "solution.*"),
250
- join2(problemDir, "input*.txt"),
251
- join2(problemDir, "output*.txt")
254
+ join2(problemDir, "testcases", "**", "*.txt")
252
255
  ],
253
256
  {
254
257
  ignoreInitial: true
@@ -317,16 +320,13 @@ function TestView({
317
320
  }
318
321
  var TestCommand = class extends Command {
319
322
  async execute(args, flags) {
320
- const problemId = getProblemId(args);
321
- const context = await resolveProblemContext(
322
- problemId !== null && problemId !== void 0 ? [problemId.toString()] : []
323
- );
323
+ const context = await resolveProblemContext(args);
324
324
  const language = await resolveLanguage(
325
- context.problemDir,
325
+ context.archiveDir,
326
326
  flags.language
327
327
  );
328
328
  await this.renderView(TestView, {
329
- problemDir: context.problemDir,
329
+ problemDir: context.archiveDir,
330
330
  language,
331
331
  watch: Boolean(flags.watch),
332
332
  timeoutMs: flags.timeoutMs
@@ -339,7 +339,7 @@ TestCommand = __decorateClass([
339
339
  description: `\uC608\uC81C \uC785\uCD9C\uB825 \uAE30\uBC18\uC73C\uB85C \uB85C\uCEEC \uD14C\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4.
340
340
  - \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uD14C\uC2A4\uD2B8 \uC2E4\uD589
341
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
342
+ - testcases/{\uBC88\uD638}/input.txt\uC640 testcases/{\uBC88\uD638}/output.txt \uD30C\uC77C\uC744 \uAE30\uBC18\uC73C\uB85C \uD14C\uC2A4\uD2B8
343
343
  - \uBB38\uC81C\uC758 \uC2DC\uAC04 \uC81C\uD55C\uC744 \uC790\uB3D9\uC73C\uB85C \uC801\uC6A9
344
344
  - --watch \uC635\uC158\uC73C\uB85C \uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8`,
345
345
  flags: [
@@ -356,7 +356,7 @@ TestCommand = __decorateClass([
356
356
  options: {
357
357
  shortFlag: "w",
358
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`
359
+ solution.*, testcases/**/*.txt \uD30C\uC77C \uBCC0\uACBD \uAC10\uC9C0`
360
360
  }
361
361
  }
362
362
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhseung/ps-cli",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "백준(BOJ) 문제 해결을 위한 통합 CLI 도구",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  "build": "tsup && node scripts/add-shebang.js",
16
16
  "prepublishOnly": "npm run build",
17
17
  "dev": "tsup --watch",
18
+ "ps": "node dist/index.js",
18
19
  "typecheck": "tsc --noEmit",
19
20
  "lint": "eslint",
20
21
  "format": "prettier --check .",