\n \u003C/label>\n \u003Cbutton>search\u003C/button>\n \u003C/form>\n\u003C/search> https://developer.mozilla.org/ja/docs/Web/HTML/Element/search",{"id":277,"title":278,"titles":279,"content":280,"level":234},"/html-tags-memory-test#cite","\u003Ccite>",[103,259],"引用先を表示する際に使用する。 \u003Cp>次の引用は有名な文学作品からのものです:\u003C/p>\n\u003Cblockquote>\n \"生きるとは呼吸することではない。行動することだ。\"\n \u003Ccite>— ジャン=ジャック・ルソー, 『エミール』\u003C/cite>\n\u003C/blockquote> https://developer.mozilla.org/ja/docs/Web/HTML/Element/cite",{"id":282,"title":283,"titles":284,"content":285,"level":234},"/html-tags-memory-test#abbr","\u003Cabbr>",[103,259],"略語を表す。 \u003Cp>\u003Cabbr>HTML\u003C/abbr>(HyperText Markup Language)\u003C/p> https://developer.mozilla.org/ja/docs/Web/HTML/Element/abbr",{"id":287,"title":288,"titles":289,"content":290,"level":234},"/html-tags-memory-test#b","\u003Cb>",[103,259],"ユーザーの注意を惹きたい場合に使用する。 ただし、\u003Cb>を太字にするという装飾目的で使用するべきではない。 単にテキストを太字にする場合は CSS の font-weight を使用し、重要な意味を持つテキストは \u003Cstrong>を使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/b",{"id":292,"title":293,"titles":294,"content":295,"level":234},"/html-tags-memory-test#bdi","\u003Cbdi>",[103,259],"ブラウザーの書字方向アルゴリズムにこのテキストが周囲のテキストから独立しているものと扱うよう指示する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/bdi",{"id":297,"title":298,"titles":299,"content":300,"level":234},"/html-tags-memory-test#bdo","\u003Cbdo>",[103,259],"現在のテキストの書字方向を上書きし、中のテキストが異なる書字方向で描画されるようにする。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/bdo",{"id":302,"title":303,"titles":304,"content":305,"level":234},"/html-tags-memory-test#data","\u003Cdata>",[103,259],"商品などを並べる際にidのような識別子を付与する際に使用する。 日付を扱う場合は\u003Cdata>ではなく\u003Ctime>を使用する。 \u003Cul>\n \u003Cli>\u003Cdata value='1'>Apple\u003C/li>\n \u003Cli>\u003Cdata value='2'>Orange\u003C/li>\n \u003Cli>\u003Cdata value='3'>Strawberry\u003C/li>\n\u003C/ul> https://developer.mozilla.org/ja/docs/Web/HTML/Element/data",{"id":307,"title":308,"titles":309,"content":310,"level":234},"/html-tags-memory-test#time","\u003Ctime>",[103,259],"時刻を表す。\ndatetime属性を一緒に使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/time",{"id":312,"title":313,"titles":314,"content":315,"level":234},"/html-tags-memory-test#dfn","\u003Cdfn>",[103,259],"定義句や文の文脈の中で定義している用語を示す。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/dfn",{"id":317,"title":318,"titles":319,"content":320,"level":234},"/html-tags-memory-test#kbd","\u003Ckbd>",[103,259],"キーボード、音声入力、その他の入力端末からのユーザーによる文字入力を表す行内の文字列の区間を表します。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/kbd",{"id":322,"title":323,"titles":324,"content":325,"level":234},"/html-tags-memory-test#mark","\u003Cmark>",[103,259],"テキストにマーカーを引くことができる。 \u003Cp>aaaa\u003Cmark>bbb\u003C/mark>\u003C/p> https://developer.mozilla.org/ja/docs/Web/HTML/Element/mark",{"id":327,"title":328,"titles":329,"content":330,"level":234},"/html-tags-memory-test#q","\u003Cq>",[103,259],"文章中に含まれるインラインの引用。 \u003Cblockquote>と異なり、テキストサイズが短い場合に使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/q",{"id":332,"title":333,"titles":334,"content":335,"level":234},"/html-tags-memory-test#ruby","\u003Cruby>",[103,259],"テキストの上下左右に表示される小さな注釈を表す。 漢字の読み等を表現する際に使用する。 後述する\u003Crp>と\u003Crt>と併せて使用する。 \u003Cruby>田中\u003Crp>(\u003C/rp>\u003Crt>Tanaka\u003C/rt>\u003Crp>)\u003C/rp>\u003C/ruby> https://developer.mozilla.org/ja/docs/Web/HTML/Element/ruby",{"id":337,"title":338,"titles":339,"content":340,"level":234},"/html-tags-memory-test#rp","\u003Crp>",[103,259],"\u003Cruby>による注釈表示に対応していないブラウザで表示する代替の括弧を提供するための要素。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/rp",{"id":342,"title":343,"titles":344,"content":345,"level":234},"/html-tags-memory-test#rt","\u003Crt>",[103,259],"注釈として表示するテキストを指定する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/rt",{"id":347,"title":348,"titles":349,"content":350,"level":234},"/html-tags-memory-test#s","\u003Cs>",[103,259],"打ち消し線で対象の要素が既に適切ではない、正確ではないことを表現する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/s",{"id":352,"title":353,"titles":354,"content":355,"level":234},"/html-tags-memory-test#del","\u003Cdel>",[103,259],"文章の中で削除されたテキスト。 \u003Cins>と併せて使用する。 \u003Cblockquote>\n \u003Cdel>const size = 100;\u003C/del>\n \u003Cins>const size = 300;\u003C/ins>\n\u003C/blockquote> https://developer.mozilla.org/ja/docs/Web/HTML/Element/del",{"id":357,"title":358,"titles":359,"content":360,"level":234},"/html-tags-memory-test#ins","\u003Cins>",[103,259],"文章の中に追加されたテキスト。 \u003Cdel>と併せて使用する。 \u003Cblockquote>\n \u003Cdel>const size = 100;\u003C/del>\n \u003Cins>const size = 300;\u003C/ins>\n\u003C/blockquote> https://developer.mozilla.org/ja/docs/Web/HTML/Element/ins",{"id":362,"title":363,"titles":364,"content":365,"level":234},"/html-tags-memory-test#samp","\u003Csamp>",[103,259],"プログラムを実行した際のサンプル出力を表す。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/samp",{"id":367,"title":368,"titles":369,"content":370,"level":234},"/html-tags-memory-test#sub","\u003Csub>",[103,259],"表記上の理由で下付き文字として表示するべきインラインテキスト。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/sub",{"id":372,"title":373,"titles":374,"content":375,"level":234},"/html-tags-memory-test#sup","\u003Csup>",[103,259],"表記上の理由で上付き文字として表示するべきインラインテキスト。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/sup",{"id":377,"title":378,"titles":379,"content":380,"level":234},"/html-tags-memory-test#u","\u003Cu>",[103,259],"テキストに対して下線を付与したいだけの場合は CSS の text-decoration を使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/u",{"id":382,"title":383,"titles":384,"content":385,"level":234},"/html-tags-memory-test#var","\u003Cvar>",[103,259],"変数名や数式を表す。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/var",{"id":387,"title":388,"titles":389,"content":390,"level":234},"/html-tags-memory-test#area","\u003Carea>",[103,259],"イメージマップの中でクリック可能な領域をあらかじめ定義する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/area",{"id":392,"title":393,"titles":394,"content":395,"level":234},"/html-tags-memory-test#track","\u003Ctrack>",[103,259],"\u003Caudio>もしくは\u003Cvideo>の子要素として使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/track",{"id":397,"title":398,"titles":399,"content":400,"level":234},"/html-tags-memory-test#wbr","\u003Cwbr>",[103,259],"ブラウザの判断で改行しても良い場所を表す。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/wbr",{"id":402,"title":403,"titles":404,"content":405,"level":234},"/html-tags-memory-test#meter","\u003Cmeter>",[103,259],"メーターを表示する際に使用する。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/meter",{"id":407,"title":408,"titles":409,"content":228,"level":196},"/html-tags-memory-test#実験的な機能","実験的な機能",[103],{"id":411,"title":412,"titles":413,"content":414,"level":234},"/html-tags-memory-test#portal","\u003Cportal>",[103,408],"別のページの内容を現在のページにプレビューとして埋め込む。 \u003Ciframe>よりも制限がある。 https://developer.mozilla.org/ja/docs/Web/HTML/Element/portal",{"id":416,"title":247,"titles":417,"content":418,"level":196},"/html-tags-memory-test#まとめ",[103],"セマンティックな実装ができるように心がけていきます。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"id":93,"title":95,"titles":420,"content":96,"level":190},[],{"id":422,"title":423,"titles":424,"content":425,"level":196},"/codebuild-token-error#エラー","エラー",[95],"「authentication required for primary source」というエラーが発生しました。",{"id":427,"title":428,"titles":429,"content":430,"level":196},"/codebuild-token-error#対処法","対処法",[95],"一度、CodeBuild と GitHub の接続を切断します。 その後、改めて CodeBuild と GitHub を接続しようとするとパーソナルアクセストークンを入力するフォームが表示されます。 有効なトークンを入力および保存をした上で再ビルドを実行すると成功しました。",{"id":432,"title":247,"titles":433,"content":434,"level":196},"/codebuild-token-error#まとめ",[95],"特にチーム開発をしている場合は、退職者のトークンを使用していないか注意しましょう。",{"id":86,"title":88,"titles":436,"content":89,"level":190},[],{"id":438,"title":439,"titles":440,"content":441,"level":196},"/search-nuxt-content#前提","前提",[88],"NuxtContent の検索機能である search は、2024年8月15日時点で実験的な機能です。 今後、仕様が変更されたり、機能そのものが外される可能性があります。 もし、search を使用する場合は nuxt.config.ts を以下のように設定する必要があります。 export default defineNuxtConfig({\n content: {\n experimental: {\n search: true\n }\n }\n})",{"id":443,"title":444,"titles":445,"content":446,"level":196},"/search-nuxt-content#検索機能の実装","検索機能の実装",[88],"今回、検索機能のロジックは composable として切り出して作成しました。 次のコードを記述するだけで検索機能を実装することができます。 export function useSearch() {\n const keyword = ref\u003Cstring>(\"\");\n\n const results = computedAsync(async () => {\n return await searchContent(keyword.value);\n }, null);\n\n return {\n keyword,\n results,\n };\n} computedAsync は、VueUse の composable です。 公式サイトの通りに進めると私の環境では検索結果がリアクティブに変更されなかったため、computedAsync を使用してリアクティブに検索結果を取得できるようにしました。",{"id":448,"title":449,"titles":450,"content":451,"level":196},"/search-nuxt-content#検索処理の呼び出し","検索処理の呼び出し",[88],"あとは作成した composable を呼び出し、検索キーワードを入力するフォームと検索結果を表示する要素を用意するだけです。 \u003Cscript lang=\"ts\" setup>\nconst { keyword, results } = useSearch();\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cinput v-model=\"keyword\" type=\"search\" placeholder=\"記事を検索する\" />\n \u003Cp>{{ results }}\u003C/p>\n\u003C/template>",{"id":453,"title":247,"titles":454,"content":455,"level":196},"/search-nuxt-content#まとめ",[88],"簡単に検索を実装することができるため、正式な機能としてリリースされることを期待したいです。 html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"id":78,"title":80,"titles":457,"content":81,"level":190},[],{"id":459,"title":460,"titles":461,"content":462,"level":196},"/monorepo#はじめに","はじめに",[80],"私はこれまでモノレポのプロダクトに携わったことがなく、モノレポ構成のイメージが持てずにいました。\nそこで、試しに自分でもモノレポ構成のリポジトリを作ってみようと思い、実際に試してみたので備忘録として残します。",{"id":464,"title":465,"titles":466,"content":467,"level":196},"/monorepo#実際に試してみる","実際に試してみる",[80],"今回、モノレポ構成の練習台に選ばれたのは Zenn の記事を管理しているリポジトリです。 https://github.com/splendente/zenn-contents/blob/main/package.json 私はリポジトリの中に Zenn の記事だけでなく、記事の中で扱ったサンプルコードを examples ディレクトリの中で管理しています。 具体的なディレクトリ構成は以下のとおりです。 /zenn-contents\n├─ articles\n│ └─ xxxx.md\n├─ books\n│ └─ xxxx.md\n├─ examples\n│ ├─ sampleA\n│ │ ├─ package.json\n│ │ └─ pnpm-lock.yaml\n│ └─ sampleB\n│ ├─ package.json\n│ └─ pnpm-lock.yaml\n├─ package.json\n└─ pnpm-lock.yaml この時、sampleA や sampleB の node_modules はルートディレクトリで pnpm install 等を実行しても作成されません。 ここで pnpm workspace の登場です。 pnpm-workspace.yaml をルートディレクトリに作成します。 内容は以下のように記述しました。 packages:\n - examples/** 最後に package.json を修正し、ルートディレクトリに配置されている package.json によって実行されるスクリプトで examples ディレクトリ配下の各プロジェクトを起動できるようにします。 \"scripts\": {\n ...省略\n \"examples:sampleA:dev\": \"pnpm run --filter=./examples/sampleA dev\",\n \"examples:sampleB:dev\": \"pnpm run --filter=./examples/sampleB dev\"\n}, --filter は -F という省略形もあるようです。",{"id":469,"title":247,"titles":470,"content":471,"level":196},"/monorepo#まとめ",[80],"手軽に pnpm の workspace を導入することができました。 個人的にはルートディレクトリでサブディレクトリのスクリプトを実行することができるという点が非常に良いと感じました。",{"id":70,"title":72,"titles":473,"content":73,"level":190},[],{"id":475,"title":460,"titles":476,"content":477,"level":196},"/github-actions#はじめに",[72],"Basic 認証を設けているサイトを運用していると、セキュリティの観点からパスワードを定期的に変更したいと考えることでしょう。 しかし、他の作業に気を取られているとついついパスワードの更新を忘れてしまいます。 そこで、定期的にパスワードを変更するというissueを作成するようにしました。",{"id":479,"title":480,"titles":481,"content":482,"level":196},"/github-actions#実際のコード","実際のコード",[72],"実行タイミングは cron にて設定します。 今回は3ヶ月毎に実行されるように設定しています。 name: update basic password\non:\n schedule:\n - cron: '0 0 1 */3 *'\n\njobs:\n create_issue:\n runs-on: ubuntu-latest\n permissions:\n issues: write\n steps:\n - name: Create Issue\n uses: actions/github-script@v6\n with:\n github-token: ${{ secrets.GITHUB_TOKEN }}\n script: |\n const issue = await github.rest.issues.create({\n owner: context.repo.owner,\n repo: context.repo.repo,\n title: 'Update basic password',\n body: `\n The basic password needs to be updated every 3 months.\n Please update the password in the password manager.\n `,\n labels: ['security']\n }); 正常に動作していると添付画像のような issue が作成されます。",{"id":484,"title":247,"titles":485,"content":486,"level":196},"/github-actions#まとめ",[72],"ワークフローがあることで定期的なタスクのリマインドも行うことができるようになり、とても便利だなと感じました。",{"id":62,"title":64,"titles":488,"content":65,"level":190},[],{"id":490,"title":491,"titles":492,"content":493,"level":196},"/get-started-with-nest-js#プロジェクトの構築","プロジェクトの構築",[64],"公式サイトでは @nestjs/cli をグローバルにインストールする手順だが、今回はグローバルを汚さないように以下のコマンドでプロジェクトを作成します。 $ npx @nestjs/cli new プロジェクト名",{"id":495,"title":496,"titles":497,"content":498,"level":196},"/get-started-with-nest-js#起動","起動",[64],"以下のコマンドでアプリを起動することができます。 pnpm start:dev",{"id":500,"title":501,"titles":502,"content":503,"level":196},"/get-started-with-nest-js#初期状態","初期状態",[64],"main.ts がエントリーファイルになっています。 NestFactory.create(AppModule)でNestJSのインスタンスを生成しています。 第一引数に渡している AppModule は、ルートモジュールです。 NestJS では1つ以上のモジュールが必要である。 ポート番号を変更し、フロントと被らないように変更します。 app.listen(process.env.PORT ?? 3001); http://localhost:3001 へアクセスし、「Hello World!」と返却されていることを確認します。 「Hello World!」を返却するメソッドは、app.service.ts 内の AppService という class の中に書かれていました。 export class AppService {\n getHello(): string {\n return 'Hello World!';\n }\n} 現時点で AppService の上に書かれている @Injectable() というデコレータがどのような役割をになっているのか理解できていません。 次に app.service.ts がどこで呼ばれているのかを確認します。 app.controller.ts と app.module.ts の2ファイルから呼ばれていることわかりました。 app.module.ts は app.service.ts だけではなく、app.controller.ts を呼び出していました。 先に app.controllers.ts を確認します。 ここで初めて気づきますが、NestJS はデコレーターを使用する場面が多いです!!! @Controller() というデコレータを定義することで app.controllers.ts で定義した class をコントローラーとして定義することができるようです。 また、引数にパスを設定することができます。 下記のように変更し、http://localhost:3001/users にアクセスすると先ほどと同じように「Hello World!」が返却されることを確認することができます。 @Controller('users') AppController という class の中では以下のようなメソッドが作成されていました。 @Get()\ngetHello(): string {\n return this.appService.getHello();\n} 基本的にコントローラーはルーティングの役割とサービスで定義したメソッドを呼び出す役割があるようです。 コントローラーでは詳細な機能の実装はせず、詳しい部分はサービスにお願いするイメージだと思います。 最後に app.module.ts を確認します。 @Module() というデコレータの引数にコントローラーとサービス、そして imports が設定されています。 @Module({\n imports: [],\n controllers: [AppController],\n providers: [AppService],\n})\nexport class AppModule {} NestJS ではこれまで確認してきた コントローラーとサービスで構築する機能の1つのモジュールという単位で呼ぶようです。 app.module.ts ではこのモジュールを定義するようです。 試しに Users というモジュールを作成しようと思います。 事前に先ほど追加した main.service.ts で定義した @Controller('users') を @Controller() に戻します。 users/index.service.ts を作成し、以下の内容を記述します。 import { Injectable } from '@nestjs/common';\nimport type { User } from './../types/index.d.ts';\n\n@Injectable()\nexport class UsersService {\n getUsers(): User[] {\n return [\n {\n id: 1,\n name: 'John',\n },\n ];\n }\n} 型情報はコントローラーでも使用するため、外部ファイルとして定義します。 type User = {\n id: number;\n name: string;\n}; 次にコントローラーを作成します。 import { Controller, Get } from '@nestjs/common';\nimport { UsersService } from './index.service';\nimport type { User } from './../types/index.d.ts';\n\n@Controller('users')\nexport class UsersController {\n constructor(private readonly usersService: UsersService) {}\n\n @Get()\n getUsers(): User[] {\n return this.usersService.getUsers();\n }\n} モジュールを作成します。 import { Module } from '@nestjs/common';\nimport { UsersController } from './index.controller';\nimport { UsersService } from './index.service';\n\n@Module({\n controllers: [UsersController],\n providers: [UsersService],\n})\nexport class UsersModule {} 最後に app.module.ts に作成したコントローラーとサービスを追記します。 別のモジュールを読み込む場合は imports に書くようです。 import { Module } from '@nestjs/common';\nimport { AppController } from './app.controller';\nimport { AppService } from './app.service';\nimport { UsersModule } from './users/index.module';\n\n@Module({\n imports: [UsersModule],\n controllers: [AppController],\n providers: [AppService],\n})\nexport class AppModule {} http://localhost:3001/usres にアクセスし、{\"id\":1,\"name\":\"John\"} というユーザーのデータが返却されていることを確認します。",{"id":505,"title":506,"titles":507,"content":508,"level":196},"/get-started-with-nest-js#環境変数の設定","環境変数の設定",[64],"NestJSで環境変数を扱うには@nestjs/configを使用します。 まずはインストールします。 pnpm add @nestjs/config 次にapp.module.tsのimportsに以下を追記します。 imports: [\n ConfigModule.forRoot({\n isGlobal: true, // グローバルに登録します\n }),\n], 次に.envファイルを用意し、環境変数を宣言します。 DATABASE_USER=test 最後に環境変数をserviceで呼び出してみます。 export class AppService {\n constructor(private configService: ConfigService) {}\n\n getHello(): string {\n return this.configService.get\u003Cstring>('DATABASE_USER');\n }\n} testという文字列が返ってくることを確認します。 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"id":55,"title":57,"titles":510,"content":58,"level":190},[],{"id":512,"title":460,"titles":513,"content":514,"level":196},"/overflow-clip#はじめに",[57],"overflow プロパティを使用する際に scroll や hidden 等を使う機会は多いですが、clip を使用する機会がなかったので調べた内容をまとめます。",{"id":516,"title":517,"titles":518,"content":519,"level":196},"/overflow-clip#overflow-clip-とは","overflow: clip; とは?",[57],"MDN を読みながら理解を深めようと思います。 https://developer.mozilla.org/ja/docs/Web/CSS/overflow#clip 挙動について以下のように記載がありました。 溢れたコンテンツは、overflow-clip-margin プロパティを使用して定義された要素のはみ出しクリップ辺で切り取られます。 hidden と同じような挙動です。 ただし、溢れたコンテンツをブラウザがどのように認識するかという点で hidden と clip には違いがあるようです。 切り取られた領域の外側に溢れたコンテンツは表示されず、ユーザーエージェントはスクロールバーを追加せず、プログラムによるスクロールも行われません。新しい整形コンテキストは作成されません。 hidden によって切り取られたコンテンツは scrollTo() メソッド等を使用することでスクロールすることができますが、clip によって切り取られたコンテンツは scrollTo() メソッド等を使ってもスクロールすることができないようです。",{"id":521,"title":522,"titles":523,"content":524,"level":196},"/overflow-clip#単一方向におけるクリップ","単一方向におけるクリップ",[57],"overflow-clip-margin を使用することでどれだけ外にはみ出すことができるかを指定することができます。 https://developer.mozilla.org/ja/docs/Web/CSS/overflow-clip-margin",{"id":526,"title":247,"titles":527,"content":528,"level":196},"/overflow-clip#まとめ",[57],"overflow: clip; は、溢れたコンテンツを切り取る CSS のプロパティで hidden とは異なり、プログラムによるスクロール操作ができない。",{"id":48,"title":50,"titles":530,"content":51,"level":190},[],{"id":532,"title":460,"titles":533,"content":534,"level":196},"/nuxt-color-mode#はじめに",[50],"Nuxt で作成したアプリケーションにダークモードを導入する際に便利なモジュールが @nuxtjs/color-mode です。 https://color-mode.nuxtjs.org/ 次のコマンドを実行し、アプリケーションに必要なモジュールを追加します。 npx nuxi module add color-mode nuxt.config.ts に以下のような設定が追加されていることを確認します。 export default defineNuxtConfig({\n modules: [\n '@nuxtjs/color-mode',\n ]\n})",{"id":536,"title":537,"titles":538,"content":539,"level":196},"/nuxt-color-mode#テーマの切り替えを実装する","テーマの切り替えを実装する",[50],"カラーのテーマを切り替えるためのボタンやセレクトボックスを用意します。 公式サイトにもコード例が載っています。 ライトテーマとダークテーマだけで良い場合は以下のようになります。 \u003Ctemplate>\n \u003Cselect v-model=\"$colorMode.preference\">\n \u003Coption value=\"light\">Light\u003C/option>\n \u003Coption value=\"dark\">Dark\u003C/option>\n \u003C/select>\n\u003C/template> 値を変更する場合に $colorMode の value ではなく、preference を変更する点に注意してください。 value を変更してもテーマの切り替えはできますが、preference の値が書きかわらないため、リロードしたりページ遷移すると変更前のテーマになってしまいます。",{"id":541,"title":542,"titles":543,"content":544,"level":196},"/nuxt-color-mode#css-変数を定義する","CSS 変数を定義する",[50],"今回は CSS で文字色や背景色を変えられるようにします。 main.css という CSS ファイルを用意します。 私は以下のような変数を定義しました。 下記を参考にご自身のアプリで使いたいテーマごとに色を設定してください。 html.light-mode{\n --primary-text-color: ライトモードで使用する文字色;\n --primary-bg-color: ライトモードでメインで使用する背景色;\n --secondary-bg-color: ライトモードでサブで使用する背景色;\n --primary-border-color: ライトモードで使用する枠線の色;\n}\n\nhtml.dark-mode {\n --primary-text-color: ダークモードで使用する文字色;\n --primary-bg-color: ダークモードでメインで使用する背景色;\n --secondary-bg-color: ダークモードでサブで使用する背景色;\n --primary-border-color: ダークモードで使用する枠線の色;\n}",{"id":546,"title":547,"titles":548,"content":549,"level":196},"/nuxt-color-mode#動作確認","動作確認",[50],"ライトモードを使用している場合です。 ダークモードを使用している場合です。",{"id":551,"title":552,"titles":553,"content":554,"level":196},"/nuxt-color-mode#storybook-を使用している場合のモック方法","Storybook を使用している場合のモック方法",[50],"Storybook で表示しようとすると @nuxtjs/color-mode の $colorMode が undefined となってしまい、エラーが表示されてしまいます。 そこで、下記のように $colorMode をモックすることで Storybook でも $colorMode を使ったコンポーネントを表示することができるようになります。 export default {\n render: () => ({\n components: { コンポーネント名 },\n setup() {\n const colorMode = {\n value: 'light',\n preference: 'light',\n unknown: false,\n forced: false,\n }\n const app = getCurrentInstance()?.appContext.app\n if (app) {\n app.config.globalProperties.$colorMode = colorMode\n }\n return {}\n },\n template: `\n \u003Cコンポーネント名 />\n `,\n }),\n}",{"id":556,"title":247,"titles":557,"content":558,"level":196},"/nuxt-color-mode#まとめ",[50],"@nuxtjs/color-mode を使用するとダークモードを簡単に導入することができました。 個人的にはダークモードにした場合の配色を考える方が大変でした。 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"id":40,"title":42,"titles":560,"content":43,"level":190},[],{"id":562,"title":563,"titles":564,"content":565,"level":196},"/prisma-error#エラーの内容","エラーの内容",[42],"npx prisma init を実行した際に以下のようなエラーメッセージが出てしまい、初期化に失敗してしまいました。 (node:30187) ExperimentalWarning: Support for loading ES Module in require() is an experimental feature and might change at any time\n(Use node --trace-warnings ... to show where the warning was created)\nError: (0 , KSe.isError) is not a function",{"id":567,"title":568,"titles":569,"content":570,"level":196},"/prisma-error#エラーの原因","エラーの原因",[42],"原因は、Node.js のバージョンでした。 私は v23.0.0 を使用していましたが、2024年12月22日の時点で Prisma が Node.js の v23 をサポートしていませんでした。",{"id":572,"title":573,"titles":574,"content":575,"level":196},"/prisma-error#エラーの対処法","エラーの対処法",[42],"v22.11.0 にバージョンを下げて再度 npx prisma init を実行すると無事に成功しました。 サポート状況は公式サイトに記載されているのでバージョンを変更する際に参考するようにしてください。",{"id":171,"title":173,"titles":577,"content":174,"level":190},[],{"id":579,"title":580,"titles":581,"content":582,"level":196},"/husky-lint-staged#husky-と-lint-staged-をインストールする","Husky と lint-staged をインストールする",[173],"Husky と lint-staged を開発環境で使用するため、次のコマンドを実行し、devDependencies に各パッケージをインストールします。 npm install -D husky lint-staged",{"id":584,"title":585,"titles":586,"content":587,"level":196},"/husky-lint-staged#パッケージ情報","パッケージ情報",[173],"2024年3月26日時点での各パッケージの最新バージョンを使用します。 バージョンの詳細については以下の表を参照してください。 パッケージ名バージョンHusky9.0.11lint-staged15.2.2",{"id":589,"title":590,"titles":591,"content":592,"level":196},"/husky-lint-staged#husky-の初期化","Husky の初期化",[173],"Husky を初期化するために次のコマンドを実行をします。 npx husky init",{"id":594,"title":595,"titles":596,"content":597,"level":196},"/husky-lint-staged#pre-commit-の追加","pre-commit の追加",[173],"pre-commit で lint-staged が実行されるようにコマンドを .husky/pre-commit に追加します。 echo \"npx lint-staged\" > .husky/pre-commit",{"id":599,"title":600,"titles":601,"content":602,"level":196},"/husky-lint-staged#packagejson-を修正","package.json を修正",[173],"最後に package.json に lint-staged の設定を追加します。 {\n \"scripts\": {\n \"prepare\": \"husky\"\n },\n \"lint-staged\": {\n \"*\": \"npm run xxxx\"\n },\n}",{"id":604,"title":247,"titles":605,"content":606,"level":196},"/husky-lint-staged#まとめ",[173],"以上で Husky と lint-staged の設定を行うことができます。 Husky のバージョンが v9 に上がったタイミングで設定周りが変更になっているため、v8 より以前のバージョンとは設定手順が異なることに注意してください。 html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"id":33,"title":35,"titles":608,"content":36,"level":190},[],{"id":610,"title":611,"titles":612,"content":613,"level":196},"/call-once-composable#callonce","callOnce",[35],"SSR もしくは CSR で処理を一度だけ実行することができる composable です。 Nuxt v3.9 から使えるようになりました。 ユースケースとしては、state の初期化を行う場合などです。 https://nuxt.com/docs/getting-started/state-management#initializing-state",{"id":615,"title":616,"titles":617,"content":618,"level":196},"/call-once-composable#ナビゲーションモード","ナビゲーションモード",[35],"Nuxt 3.15 で新たに追加された機能です。 https://nuxt.com/blog/v3-15#%EF%B8%8F-navigation-mode-for-callonce 公式サイトにも記載がありますが、下記のように { mode: 'navigation' } を指定することでナビゲーションモードを有効にすることができます。 await callOnce(() => counter.value++, { mode: 'navigation' })",{"id":620,"title":621,"titles":622,"content":623,"level":196},"/call-once-composable#middleware-と-callonce-のナビゲーションモードの違いについて","middleware と callOnce のナビゲーションモードの違いについて",[35],"ページ遷移の度に実行することができるという説明を聞くと、middleware と役割が似ているように感じます。 大きな違いとして、処理が実行されるタイミングが異なります。 middleware で定義される処理はナビゲーションの前に実行されます。 そのため、middleware には、ログイン状態のチェックや閲覧権限のチェック等の処理を定義するのが向いています。 逆に callOnce のナビゲーションモードはナビゲーションのタイミングで実行されるため、タイミングとしては middleware よりも後に実行されます。 そのため、callOnce には、状態等のデータの初期化等の処理を定義するのが向いています。 html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":23,"title":25,"titles":625,"content":26,"level":190},[],{"id":627,"title":428,"titles":628,"content":629,"level":196},"/option-display-none#対処法",[25],"よくある対処法として、span 要素で option 要素を囲み、span 要素に対して display: none; を指定する方法があります。 しかし、select 要素の子要素として span 要素を指定することは HTML の仕様に従わないため、他の方法を模索するべきだと思います。 CSS で指定する方法は諦め、動的に要素を追加したり、削除したりする方法が良いと思います。",{"id":14,"title":16,"titles":631,"content":17,"level":190},[],{"id":633,"title":423,"titles":634,"content":635,"level":196},"/fix-msw-github-pages-error#エラー",[16],"Storybook を GitHub Pages へデプロイした際に下記のようなエラーが表示されてしまいました。 [MSW] Failed to register a Service Worker for scope ('https://splendente.github.io/') with script ('https://splendente.github.io/mockServiceWorker.js'): Service Worker script does not exist at the given path. MSW(Mock Service Worker)のスクリプトファイルである mockServiceWorker.js が見つからないことが原因です。 GitHub Pages にデプロイすると、URLが https://{ユーザー名}.github.io/{リポジトリ名}/ となります。 そのため、mockServiceWorker.js のパスがルートに向いてるとエラーのようにファイルが見つけられないようです。",{"id":637,"title":638,"titles":639,"content":640,"level":196},"/fix-msw-github-pages-error#対象法","対象法",[16],"GitHub Pages では mockServiceWorker.js のパスを /リポジトリ名/mockServiceWorker.js と指定すると解消します。 .storybook/preview.(js or ts) を下記のように変更します。 let options = {};\nif (location.hostname === \"自分のユーザー名.github.io\") {\n options = {\n serviceWorker: {\n url: \"/リポジトリ名/mockServiceWorker.js\",\n },\n };\n}\n\n// Initialize MSW\ninitialize(options); html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":6,"title":8,"titles":642,"content":9,"level":190},[],{"id":644,"title":645,"titles":646,"content":647,"level":196},"/nuxt-og-image#nuxt-og-image-について","nuxt-og-image について",[8],"nuxt-og-image は、OGP 画像を簡単に作成することができる Nuxt のモジュールです。 ただし、注意点として英語以外の言語に対応していません。 そのため、私のように日本語でブログの記事を執筆している場合、 OGP 画像に表示させるテキストが文字化けしてしまいます。",{"id":649,"title":428,"titles":650,"content":651,"level":196},"/nuxt-og-image#対処法",[8],"公式ドキュメントの中に対処法が書いてありました。 日本語を使用する場合、以下のように nuxt.Config.ts を修正します。 export default defineNuxtConfig({\n ogImage: {\n fonts: [\n 'Noto+Sans+JP:400',\n ]\n }\n}) Nuxt Dev Tools で文字化けが解消されていることを確認します。 フォントは、${name}:${weight} の形式で Google フォントを指定することができます。 例えば、文字を太くしたい場合は下記のように指定します。 export default defineNuxtConfig({\n ogImage: {\n fonts: [\n 'Noto+Sans+JP:700', \n ]\n }\n})",{"id":653,"title":247,"titles":654,"content":655,"level":196},"/nuxt-og-image#まとめ",[8],"nuxt-og-image で英語以外のテキストを使用する場合は明示的にフォントを指定する必要がある",{"id":657,"title":658,"titles":659,"content":660,"level":196},"/nuxt-og-image#参考記事","参考記事",[8],"https://nuxtseo.com/docs/og-image/guides/non-english-localeshttps://nuxtseo.com/docs/og-image/guides/custom-fonts html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":163,"title":165,"titles":662,"content":663,"level":190},[],"フロントエンドのテスト手法の1つであるVisual Regression Testのツールであるstorycapの実行時間を短縮するための情報についてまとめる。 Storybookを事前にBuildするテスト対象を絞る並列実行する",{"id":154,"title":156,"titles":665,"content":666,"level":190},[],"ElysiaJSについての個人的なメモ。 Bunのフレームワーク公式サイトExpressよりも21倍速い人間工学に基づいた人間のためのフレームワークEnd-to-Endで型セーフである",{"id":668,"title":669,"titles":670,"content":671,"level":196},"/get-started-with-elysiajs#bunのインストール","Bunのインストール",[156],"まだBunをインストールしたことがなかったため、公式サイトを参照し、下記のコマンドを実行します。 brew install oven-sh/bun/bun 2024年4月7日時点では、v1.1.2がインストールされました。",{"id":673,"title":674,"titles":675,"content":676,"level":196},"/get-started-with-elysiajs#elysiajsのインストール","ElysiaJSのインストール",[156],"bun create elysia アプリ名",{"id":678,"title":679,"titles":680,"content":681,"level":196},"/get-started-with-elysiajs#アプリの起動","アプリの起動",[156],"# To get started, run:\n\n cd アプリ名\n bun run src/index.ts インストール時に表示されたメッセージに従って下記を実行します。 bun run src/index.ts 上記では、メッセージに従いましたが、bun devでも起動できます。 localhost:3000 へアクセスし、「Hello Elysia」と表示されることを確認します。",{"id":683,"title":684,"titles":685,"content":686,"level":196},"/get-started-with-elysiajs#indextsを覗いてみる","index.tsを覗いてみる",[156],"import { Elysia } from \"elysia\";\n\nconst app = new Elysia().get(\"/\", () => \"Hello Elysia\").listen(3000);\n\nconsole.log(\n `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`\n); コンソールに「🦊 Elysia is running at localhost:3000」が表示されることを確認します。",{"id":688,"title":689,"titles":690,"content":691,"level":196},"/get-started-with-elysiajs#マニュアルのインストール","マニュアルのインストール",[156],"bun add elysia dependencyにelysiaが追加されるため、npmスクリプトを下記のように変更します。 \"scripts\": {\n \"dev\": \"bun run --watch src/index.ts\",\n \"build\": \"bun build src/index.ts\",\n \"start\": \"NODE_ENV=production bun src/index.ts\",\n \"test\": \"bun test\"\n}, testコマンドは、既に存在していため、追加ではなく更新しました。 devコマンドは、開発モードで起動し、オートリロードが効くbuildコマンドは、プロダクションビルドを実行するstartコマンドは、プロダクションサーバーで起動するtestコマンドは、テストを実行してくれる",{"id":693,"title":694,"titles":695,"content":696,"level":196},"/get-started-with-elysiajs#公式おすすめのディレクトリ構成","公式おすすめのディレクトリ構成",[156],"elysia-app\n ├── src\n │ ├── index.ts // エントリーポイント\n │ ├── setup.ts // [wip]調べる\n │ ├── controllers // [wip]調べる\n │ ├── libs // 共通処理のファイルを格納する\n │ ├── models // [wip]調べる\n │ └── types // 型情報を記載したファイルを格納する\n ├── test // テストファイルを格納する\n ├── bun.lockb\n ├── node_modules\n ├── package.json\n └── tsconfig.json html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"id":146,"title":148,"titles":698,"content":699,"level":190},[],"Honoについての個人的なメモ。 公式サイト Honoは、CDNのエッジで動く高速なWebフレームワークです。 Cloudflareだけでなく、FastlyやAWS Lambda、Vercelなどでも動くようです。",{"id":701,"title":702,"titles":703,"content":704,"level":196},"/get-started-with-hono#honoのインストール","Honoのインストール",[148],"npmを使用してインストールする場合、下記のコマンドを実行します。 npm create hono@latest my-app プロジェクトを作成したら下記のコマンドを実行し、アプリを起動します。 npm run dev http://localhost:8787にアクセスし、「Hello Hono!」と表示されることを確認します。 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":139,"title":141,"titles":706,"content":707,"level":190},[],"OSSへのコントリビュートという実績を解除したのできっかけなどをまとめる。 本日、OSSへのコントリビュートという実績を解除しました。 https://github.com/vuejs/docs/pull/2814",{"id":709,"title":710,"titles":711,"content":712,"level":196},"/first-oss-contribution#きっかけ","きっかけ",[141],"Vue.jsのTransitionとTransition Groupの違いを調査するため、公式ドキュメントを漁っていた時のことです。 サンプルコードでulタグ直下の要素がliタグではなく、divタグになっていることに気づいたことがきっかけです。",{"id":714,"title":715,"titles":716,"content":228,"level":196},"/first-oss-contribution#ossコントリビュートまでの軌跡","OSSコントリビュートまでの軌跡",[141],{"id":718,"title":719,"titles":720,"content":721,"level":234},"/first-oss-contribution#issueを作成する","issueを作成する",[141,715],"既に同じ問題に気づいている人がいる可能性があるため、Vue.jsの公式ドキュメントのリポジトリのissueを漁ってみました。 修正したい内容である「Transition Group」や「ul」、「list」等の単語を検索ワードとして使いつつ、issueを確認しましたが、重複するissueを見つけることができませんでした。 既にPull Requestが作成されている可能性もあるのでPull Requestも確認しました。 その後も調査しましたが、どこにも重複するissueやPull Requestを見つけられなかったため、issueを作成することにしました。 Google翻訳等を活用し、issueを作成後、わずか数分で「bug」と「contribution welcome」というタグをコアチームメンバーの方が設定してくれました。",{"id":723,"title":724,"titles":725,"content":726,"level":234},"/first-oss-contribution#pull-requestを作成する","Pull Requestを作成する",[141,715],"次に私はこのissueを自分が担当していいのかどうかわからず、路頭に迷いました。 「君が担当者だ!」とアサインされるのを待つべきか勝手にPRを送るってもいいのか...。 リポジトリに対する権限的にissueに対して自らをアサインすることができなかったため、ルールなどが記載されたドキュメントを探すことにしました。 README.mdなどの様々なドキュメントを漁りましたが、欲しい情報を見つけることはできませんでした。 他のissue等を覗きましたが、明確に担当者をアサインしているケースを見つけることができず、逆にissueを作成した人がそのままPull Requestを作成しているケースが多かったことと「contribution welcome」というタグを信じ、ダメ元でPull Requestを送ることにしました。 OSSへのコントリビュートが初めてだったため、fork等の慣れない作業は都度調べながら行いました。 修正内容は軽微だったため、すぐに修正を終え、Pull Requestを作成しました。",{"id":728,"title":729,"titles":730,"content":731,"level":234},"/first-oss-contribution#レビュー-merge","レビュー & Merge",[141,715],"Pull Request作成後、すぐに自分が作成したissueに反応してくださったコアメンバーの方がレビューおよびApproveをしてくださり、そのままMergeとなりました。 (初歩的なミスもカバーしてくれました。) 以上で初のOSSコントリビュートが無事に終了することができました。",{"id":733,"title":734,"titles":735,"content":736,"level":196},"/first-oss-contribution#ossコントリビュートを経験してみた感想","OSSコントリビュートを経験してみた感想",[141],"OSSと聞くと少し構えてしまうが、それよりもチャレンジしてみて良かったという気持ちが強いです。 簡単な修正でしたが、初めてVitePressを触ってみることができたりと得られた収穫が大きいです。 今回はいきなりPull Requestを送りましたが、リポジトリのルールはしっかり確認し、もし不安であればissueなどでコメントして事前に担当していいか確認するのが良いと思います。 これを機にOSS活動も頑張っていきたいです。",{"id":132,"title":134,"titles":738,"content":135,"level":190},[],{"id":740,"title":741,"titles":742,"content":743,"level":196},"/difference-between-defineexpose-and-provide-and-inject#defineexpose-について","defineExpose について",[134],"defineExpose は、Vue.js のコンパイラーマクロで子コンポーネントの script setup 構文で定義されたプロパティを親コンポーネント、もしくは外部のスクリプトファイルに対して公開する機能です。 script setup 構文は、デフォルトでスコープが閉じているため、親コンポーネントや外部のスクリプトファイルは通常アクセスすることができません。 そのため、親コンポーネントから子コンポーネントの変数にアクセスしたい場合などは defineExpose を使用する必要があります。",{"id":745,"title":746,"titles":747,"content":748,"level":234},"/difference-between-defineexpose-and-provide-and-inject#使用例","使用例",[134,741],"defineExpose を使用した場合、子コンポーネントは下記のように書くことができます。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\n\nconst count = ref\u003Cnumber>(0);\n\nconst increment = () => {\n count.value++;\n};\n\ndefineExpose({\n increment,\n});\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cp>{{ count }}\u003C/p>\n \u003C/div>\n\u003C/template> count というリアクティブデータが定義されおり、increment という関数が実行されると数値が1ずつ増えるというシンプルなコンポーネントです。 公開したいプロパティを defineExpose 内に書くだけで外部に公開することができます。 increment という関数を外部に公開したい場合、下記のように書くことができます。 defineExpose({\n increment,\n}); defineExpose を使用した子コンポーネントを呼び出す親コンポーネントは下記のように書くことができます。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst count = ref\u003CInstanceType\u003Ctypeof Count> | null>(null);\n\nconst execIncrement = () => {\n count.value?.increment();\n};\n\u003C/script>\n\n\u003Ctemplate>\n \u003CCount ref=\"count\" />\n \u003Cbutton type=\"button\" @click=\"execIncrement\">increment\u003C/button>\n\u003C/template> テンプレート参照を使用することで、子コンポーネントで defineExpose を使用して定義した increment という関数にアクセスすることができます。 上記の例では、increment と書かれたボタンをクリックすると子コンポーネントの count というリアクティブデータが1つずつ増えることを確認することができます。",{"id":750,"title":751,"titles":752,"content":228,"level":196},"/difference-between-defineexpose-and-provide-and-inject#provide-inject-について","Provide & Inject について",[134],{"id":754,"title":755,"titles":756,"content":757,"level":234},"/difference-between-defineexpose-and-provide-and-inject#provide","Provide",[134,751],"親または祖先にあたるコンポーネントから子または子孫にあたるコンポーネントに対してデータやメソッドを共有する機能です。 Provideは、defineExpose とは異なり、子また子孫にあたるコンポーネントから親または祖先にあたるコンポーネントに対してデータやメソッドを共有することはできません。 また、外部のスクリプトファイルに対してもデータやメソッドを共有することはできない点も defineExpose と異なる点です。",{"id":759,"title":760,"titles":761,"content":762,"level":234},"/difference-between-defineexpose-and-provide-and-inject#inject","Inject",[134,751],"子または子孫にあたるコンポーネントが親または祖先にあたるコンポーネントから共有されたデータやメソッドを受け取る機能です。",{"id":764,"title":746,"titles":765,"content":766,"level":234},"/difference-between-defineexpose-and-provide-and-inject#使用例-1",[134,751],"Provide と Inject を使用する場合、下記のように書くことができます。 Provide を定義する親コンポーネントです。 先ほどの defineExpose で使用した例を再利用しています。 注意点としては、defineExpose とは異なり、親コンポーネントであることです。 \u003Cscript setup lang=\"ts\">\nimport { ref, provide } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst count = ref\u003Cnumber>(0);\n\nconst increment = () => {\n count.value++;\n};\n\nprovide(\"increment\", increment);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cp>{{ count }}\u003C/p>\n \u003CCount />\n\u003C/template> 親コンポーネントから Provide で共有された increment という関数を Inject で受け取るために子コンポーネントは下記のように書きます。 \u003Cscript setup lang=\"ts\">\nimport { inject } from \"vue\";\n\nconst increment = inject\u003C() => void>(\"increment\")!;\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cbutton type=\"button\" @click=\"increment()\">increment\u003C/button>\n \u003C/div>\n\u003C/template> 子コンポーネントで定義した increment 書かれたボタンをクリックすると親コンポーネントの count というリアクティブデータが1つずつ増えることを確認することができます。 Provide と Injectは、子または子孫コンポーネントから親または祖先コンポーネントに対して情報を共有することはできません。 例えば、下記のコードはエラーになります。 子コンポーネントで定義した increment という関数を Provide で定義します。 \u003Cscript setup lang=\"ts\">\nimport { ref, provide } from \"vue\";\n\nconst count = ref(0);\n\nconst increment = () => {\n count.value++;\n};\n\nprovide(\"increment\", increment);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cp>{{ count }}\u003C/p>\n\u003C/template> 次に親コンポーネントで Inject を使用して increment という関数にアクセスしようとしてみます。 \u003Cscript setup lang=\"ts\">\nimport { inject } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst increment = inject\u003C() => void>(\"increment\")!;\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003CCount />\n \u003Cbutton type=\"button\" @click=\"increment()\">increment\u003C/button>\n \u003C/div>\n\u003C/template> 上記のコードを実行すると次のエラーが表示されるはずです。 Vue warn: injection \"increment\" not found.",{"id":768,"title":247,"titles":769,"content":770,"level":196},"/difference-between-defineexpose-and-provide-and-inject#まとめ",[134],"defineExpose を使用することで親または祖先コンポーネントだけでなく、外部のスクリプトファイルに対してもプロパティを公開することができます。 Provide は、親または祖先コンポーネントから子または子孫コンポーネントに対して情報を共有する機能であり、子または子孫コンポーネントから親または祖先コンポーネントに対して情報を共有することはできません。 Inject は、子または子孫にあたるコンポーネントが親または祖先にあたるコンポーネントから共有されたデータやメソッドを受け取るのみで外部にプロパティを公開したり、親または祖先コンポーネントに対して情報を共有することはできません。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":123,"title":125,"titles":772,"content":126,"level":190},[],{"id":774,"title":775,"titles":776,"content":777,"level":196},"/run-workflow-with-secrets-in-dependabot#secrets-を使用したワークフローが失敗する","secrets を使用したワークフローが失敗する",[125],"先日、chromatic を GitHub Actions で実行するようなワークフローを追加しました。 自分で変更を加え、プッシュをした際やプルリクエストを作成した際に chromatic のワークフローが実行されることは確認していたのですが、Dependabot によってプルリクエストが作成された際にワークフローが失敗してしまいました。 実際に表示されたエラーメッセージを確認すると、chromatic にデプロイを実行する際に必要なトークンが取得できていないことが分かります。 ✖ Missing project token\n\nSign in to https://www.chromatic.com/start and create a new project,\nor find your project token on the Manage screen in an existing project.\nSet your project token as the CHROMATIC_PROJECT_TOKEN environment variable\nor pass the --project-token command line option. しかし、GitHub の secrets に CHROMATIC_PROJECT_TOKEN というトークン保存しているため、トークンを読み込めないという事象に戸惑いました。 調査を進める中で Dependabot は actions に設定されている secrets を読み込むことができないということがわかりました。",{"id":779,"title":780,"titles":781,"content":782,"level":196},"/run-workflow-with-secrets-in-dependabot#解決策","解決策",[125],"Dependabot secrets を使用することで解決することができます。 次の画像を参考に Settings の Security から Secrets and variables へアクセスし、「New repository secret」というボタンを押下することで chromatic の実行時に必要なトークンを設定することができます。 設定することができたら、失敗した chromatic のワークフローを再実行し、無事に成功することを確認します。",{"id":115,"title":117,"titles":784,"content":118,"level":190},[],{"id":786,"title":787,"titles":788,"content":789,"level":196},"/toref-and-torefs#toref-について","toRef について",[117],"toRef は、引数として与えられた値から ref を作成します。 例えば、props として受け取った値を変更したい場合があると思います。 props は、一方向のバインディングを形成します。 親コンポーネントで値が変更した場合、子コンポーネントで受け取った props の値は変更されますが、子コンポーネントで受け取った props の値をを変更することはできません。 次のコードはボタンをクリックしても値が変更されません。 \u003Cscript setup lang=\"ts\">\ndefineProps\u003C{ count: number }>();\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template> このような場合に toRef を使用することで受け取った props の値を変更することができます。 toRef を使用することで受け取った props から ref を作成することでボタンをクリックした際に値が変更されるようになります。 \u003Cscript setup lang=\"ts\">\nimport { toRef } from \"vue\";\n\nconst props = defineProps\u003C{ count: number }>();\n\nconst count = toRef(props.count);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template> この時、toRef は新たに ref を定義するため、親で保持する値が変更されないことに注意してください。 次の親コンポーネントで定義された count の値は変更されません。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport Child from \"./components/Child.vue\";\n\nconst count = ref\u003Cnumber>(0);\n\u003C/script>\n\n\u003Ctemplate>\n \u003CChild :count=\"count\" />\n \u003Cp>count is {{ count }}\u003C/p>\n\u003C/template> また、readonly なリアクティブデータとして定義することもできます。 次のコードは先ほどとは異なり、ボタンをクリックしても count の値が変更されることはありません。 \u003Cscript setup lang=\"ts\">\nimport { toRef } from \"vue\";\n\nconst props = defineProps\u003C{ count: number }>();\n\nconst count = toRef(() => props.count);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template>",{"id":791,"title":792,"titles":793,"content":794,"level":196},"/toref-and-torefs#torefs-について","toRefs について",[117],"toRefs は、reactive によって宣言されたリアクティブなオブジェクトの各プロパティを toRef によって ref に変換します。 \u003Cscript setup lang=\"ts\">\nimport { reactive, toRefs } from \"vue\";\n\nconst man = reactive({\n age: 20,\n});\n\nconst stateAsRefs = toRefs(man);\n\nman.age++;\nconsole.log(man.age); // 21\nconsole.log(stateAsRefs.age.value); // 21\n\nstateAsRefs.age.value++;\nconsole.log(man.age); // 22\nconsole.log(stateAsRefs.age.value); // 22\n\u003C/script> リアクティビティを損なうことなく、個々の ref を生成することができるため、どちらかの値が変更されるともう片方の値も書き変わります。 toRefs を使用して ref を生成する場合、.value を使用してアクセスする必要があることに注意してください。",{"id":796,"title":247,"titles":797,"content":798,"level":196},"/toref-and-torefs#まとめ",[117],"toRef は引数から ref を生成し、toRefs はリアクティブなオブジェクトから toRef を使用して個別の ref を生成します。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",[800,802,803,805,806,807,808,809,811,813,815,816,818,820,821,822,823,824,825,826,827,829,830],{"createdAt":184,"updatedAt":801},"2024-04-23",{"createdAt":112,"updatedAt":112},{"createdAt":105,"updatedAt":804},"2024-07-23",{"createdAt":97,"updatedAt":97},{"createdAt":90,"updatedAt":90},{"createdAt":82,"updatedAt":82},{"createdAt":74,"updatedAt":74},{"createdAt":66,"updatedAt":810},"2024-12-18",{"createdAt":59,"updatedAt":812},"2024-12-01",{"createdAt":52,"updatedAt":814},"2025-01-06",{"createdAt":44,"updatedAt":44},{"createdAt":175,"updatedAt":817},"2024-04-29",{"createdAt":37,"updatedAt":819},"2024-12-27",{"createdAt":27,"updatedAt":27},{"createdAt":18,"updatedAt":18},{"createdAt":10,"updatedAt":10},{"createdAt":167,"updatedAt":167},{"createdAt":158,"updatedAt":801},{"createdAt":150,"updatedAt":150},{"createdAt":143,"updatedAt":801},{"createdAt":136,"updatedAt":828},"2024-05-25",{"createdAt":127,"updatedAt":127},{"createdAt":119,"updatedAt":831},"2024-05-28",[833,835,837,839,841,843,845,847,849,851,853,855,857,859,861,863,865,867,869,871,873,875,877],{"tags":834},[12],{"tags":836},[99],{"tags":838},[29],{"tags":840},[99],{"tags":842},[12],{"tags":844},[84],{"tags":846},[76],{"tags":848},[68],{"tags":850},[30],{"tags":852},[12],{"tags":854},[46],{"tags":856},[177,178],{"tags":858},[12],{"tags":860},[29,30,31],{"tags":862},[20,21],{"tags":864},[12],{"tags":866},[20,169],{"tags":868},[160,161],{"tags":870},[152],{"tags":872},[121],{"tags":874},[121],{"tags":876},[129,130],{"tags":878},[121],["Reactive",880],{"$snuxt-i18n-meta":881,"$scolor-mode":882,"$ssite-config":886},{},{"preference":883,"value":883,"unknown":884,"forced":885},"system",true,false,{"_priority":887,"currentLocale":891,"defaultLocale":891,"env":892,"name":893,"url":894},{"name":888,"env":889,"url":888,"defaultLocale":890,"currentLocale":890},-3,-15,-2,"ja_JP","production","blog by Hikaru Kobayashi","https://www.hikaru-kobayashi.me",["Set"],["ShallowReactive",897],{"search-data":-1,"articles":-1,"content":-1,"tags":-1},"/"]