diff --git a/src/client/pages/items/show.tsx b/src/client/pages/items/show.tsx index 89843fd..3fd9d43 100644 --- a/src/client/pages/items/show.tsx +++ b/src/client/pages/items/show.tsx @@ -124,7 +124,7 @@ export const ItemsShow = () => { error -
{errorMessage}
+
{errorMessage}

))} @@ -184,7 +184,3 @@ const errorStyle = css({ display: "flex", marginTop: getSpace(3 / 2), }); - -const errorMessageBodyStyle = css({ - whiteSpace: "pre", -}); diff --git a/src/commands/index.ts b/src/commands/index.ts index 572baa3..b6d4074 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,3 +1,4 @@ +import { handleError } from "../lib/error-handler"; import { packageUpdateNotice } from "../lib/package-update-notice"; import { help, helpText } from "./help"; import { init } from "./init"; @@ -38,5 +39,11 @@ export const exec = async (commandName: string, commandArgs: string[]) => { console.log(updateMessage); } - commands[commandName](commandArgs); + try { + await commands[commandName](commandArgs); + } catch (err) { + console.error(err); + await handleError(err as Error); + process.exit(1); + } }; diff --git a/src/commands/publish.ts b/src/commands/publish.ts index 91b29fa..2b0e9fa 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -65,10 +65,15 @@ export const publish = async (argv: string[]) => { return acc; }, [] as { name: string; errors: string[] }[]); if (invalidItemMessages.length > 0) { - console.error("Validation error:"); + const chalk = (await import("chalk")).default; invalidItemMessages.forEach((msg) => { - console.error(msg.name, msg.errors); + msg.errors.forEach((err) => { + const errorName = chalk.red.bold(msg.name + ":"); + const errorDescription = chalk.red(err); + console.error(`${errorName} ${errorDescription}`); + }); }); + process.exit(1); } diff --git a/src/lib/check-frontmatter-type.ts b/src/lib/check-frontmatter-type.ts index 2f98e39..00d077f 100644 --- a/src/lib/check-frontmatter-type.ts +++ b/src/lib/check-frontmatter-type.ts @@ -65,7 +65,7 @@ const checkOrganizationUrlName: CheckType = { const checkSlide: CheckType = { getMessage: () => - "slideの設定はtrue/falseで入力してください\n\n【破壊的な変更がありました】\n詳しくは以下のリリースをご確認ください\nhttps://github.com/increments/qiita-cli/releases/tag/v0.5.0", + "slideの設定はtrue/falseで入力してください(破壊的な変更がありました。詳しくはリリースをご確認ください https://github.com/increments/qiita-cli/releases/tag/v0.5.0)", isValid: ({ slide }) => { return typeof slide === "boolean"; }, diff --git a/src/lib/error-handler.ts b/src/lib/error-handler.ts new file mode 100644 index 0000000..a3939ac --- /dev/null +++ b/src/lib/error-handler.ts @@ -0,0 +1,86 @@ +import { + QiitaBadRequestError, + QiitaFetchError, + QiitaForbiddenError, + QiitaInternalServerError, + QiitaNotFoundError, + QiitaRateLimitError, + QiitaUnauthorizedError, + QiitaUnknownError, +} from "../qiita-api"; + +export const handleError = async (error: Error) => { + const chalk = (await import("chalk")).default; + + switch (error.name) { + // Qiita API + case QiitaFetchError.name: + console.error( + chalk.red.bold( + "Qiita APIへのリクエストでネットワークエラーが発生しました" + ) + ); + console.error( + chalk.red(" インターネットに接続されているかご確認ください") + ); + break; + case QiitaBadRequestError.name: + console.error(chalk.red.bold("Qiita APIへのリクエストに失敗しました")); + console.error(chalk.red(" 記事ファイルに不備がないかご確認ください")); + break; + case QiitaUnauthorizedError.name: + console.error(chalk.red.bold("Qiitaの認証に失敗しました")); + console.error( + chalk.red(" loginコマンドでQiitaにログインしているかご確認ください") + ); + console.error( + chalk.red(" Qiitaのアクセストークンが正しいかご確認ください") + ); + break; + case QiitaForbiddenError.name: + console.error(chalk.red.bold("Qiita APIへのリクエストに失敗しました")); + console.error( + chalk.red(" Qiitaのアクセストークンが正しいかご確認ください") + ); + console.error(chalk.red("")); + break; + case QiitaNotFoundError.name: + console.error(chalk.red.bold("記事が見つかりませんでした")); + console.error( + chalk.red(" Qiita上で記事が削除されていないかご確認ください") + ); + break; + case QiitaRateLimitError.name: + console.error(chalk.red.bold("Qiita APIのレートリミットに達しました")); + console.error(chalk.red(" しばらく時間を置いてから再度お試しください")); + break; + case QiitaInternalServerError.name: + console.error(chalk.red.bold("Qiitaのサーバーでエラーが発生しました")); + console.error(chalk.red(" しばらく時間を置いてから再度お試しください")); + break; + case QiitaUnknownError.name: + console.error( + chalk.red.bold("Qiita APIへのリクエストで不明なエラーが発生しました") + ); + console.error( + chalk.red( + " バグの可能性がある場合は、Qiita Discussionsよりご報告いただけると幸いです" + ) + ); + console.error( + chalk.red(" https://github.com/increments/qiita-discussions") + ); + break; + + default: + console.error(chalk.red.bold(`エラーが発生しました (${error.message})`)); + console.error( + chalk.red( + " バグの可能性がある場合は、Qiita Discussionsよりご報告いただけると幸いです" + ) + ); + console.error( + chalk.red(" https://github.com/increments/qiita-discussions") + ); + } +}; diff --git a/src/qiita-api/errors.ts b/src/qiita-api/errors.ts new file mode 100644 index 0000000..f925eaa --- /dev/null +++ b/src/qiita-api/errors.ts @@ -0,0 +1,55 @@ +export class QiitaFetchError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaFetchError"; + } +} + +export class QiitaBadRequestError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaBadRequestError"; + } +} + +export class QiitaUnauthorizedError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaUnauthorizedError"; + } +} + +export class QiitaForbiddenError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaForbiddenError"; + } +} + +export class QiitaNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaNotFoundError"; + } +} + +export class QiitaRateLimitError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaRateLimitError"; + } +} + +export class QiitaInternalServerError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaInternalServerError"; + } +} + +export class QiitaUnknownError extends Error { + constructor(message: string) { + super(message); + this.name = "QiitaUnknownError"; + } +} diff --git a/src/qiita-api/index.ts b/src/qiita-api/index.ts index 7571661..655e9ee 100644 --- a/src/qiita-api/index.ts +++ b/src/qiita-api/index.ts @@ -1,6 +1,18 @@ import { URL, URLSearchParams } from "node:url"; +import { + QiitaBadRequestError, + QiitaFetchError, + QiitaForbiddenError, + QiitaInternalServerError, + QiitaNotFoundError, + QiitaRateLimitError, + QiitaUnauthorizedError, + QiitaUnknownError, +} from "./errors"; import { qiitaApiDebugger } from "./lib/debugger"; +export * from "./errors"; + export interface Item { body: string; id: string; @@ -65,7 +77,7 @@ export class QiitaApi { }); } catch (err) { console.error(err); - throw new Error("NetworkError"); + throw new QiitaFetchError((err as Error).message); } if (response.ok) { @@ -78,8 +90,8 @@ export class QiitaApi { } } + const responseBody = await response.text(); if (qiitaApiDebugger.enabled) { - const responseBody = await response.text(); qiitaApiDebugger( "request failed", JSON.stringify({ @@ -89,21 +101,22 @@ export class QiitaApi { ); } + const errorMessage = responseBody.slice(0, 100); switch (response.status) { case 400: - throw new Error("QiitaBadRequestError"); + throw new QiitaBadRequestError(errorMessage); case 401: - throw new Error("QiitaUnauthorizedError"); + throw new QiitaUnauthorizedError(errorMessage); case 403: - throw new Error("QiitaForbiddenError"); + throw new QiitaForbiddenError(errorMessage); case 404: - throw new Error("QiitaNotFoundError"); + throw new QiitaNotFoundError(errorMessage); case 429: - throw new Error("QiitaRateLimitError"); + throw new QiitaRateLimitError(errorMessage); case 500: - throw new Error("QiitaInternalServerError"); + throw new QiitaInternalServerError(errorMessage); default: - throw new Error("QiitaUnknownError"); + throw new QiitaUnknownError(errorMessage); } } diff --git a/src/server/app.ts b/src/server/app.ts index 4d97fd1..9f2de35 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -35,7 +35,7 @@ export async function startServer() { const port = userConfig.port; const host = "localhost"; - return new Promise((resolve) => { + return new Promise((resolve, reject) => { server .listen(port, host) .once("listening", () => { @@ -43,8 +43,8 @@ export async function startServer() { resolve(server); }) - .once("error", () => { - throw new Error("Failed to start server"); + .once("error", (err) => { + reject(err); }); }); }