Depending on the Time and Situation

時と場合によりけり 〜 日々のアップデートとイノベーションに翻弄され彷徨える IT エンジニアの覚書

Astro を Codespaces で構築する

概要

静的サイトジェネレーター( SSG: Static Site Generator )として人気急上昇中の Astro 。開発環境を GitHub Codespaces に構築してみます。

解説動画


www.youtube.com

この記事の目次

GitHub リポジトリ作成

わかりやすい名前で、GitHubリポジトリを作成します。今回は、とりあえず astro という名前でリポジトリを作成しました。Add a README file や Add .gitignore については、必要であれば、適宜追加してください。(今回は、Add .gitignore のプルダウンのところで、Node を指定し、.gitignore を追加しておきました。一応、README も追加しておきました。)

Codespace 作成

作成した GitHub リポジトリの <>Code▼ ボタンをクリックします。Codespaces のタブにある Create codespace on main ボタンをクリックします。

Astro インストール

まずはバージョンを確認してみます。

$ node -v
v19.7.0

では、Astro のインストールです。

$ npm create astro@latest

このブログを執筆している時点でインストールされる最新バージョンは、2.1.3 となります。表示される質問に答えていけば、簡単にインストールが完了します。今回は、ディレクトを「 tutrial 」。依存関係をすべてインストールし、TypeScript を使用することにしました。

Need to install the following packages:
  create-astro@3.1.0
Ok to proceed? (y) 

╭─────╮  Houston:
│ ◠ ◡ ◠  Let's build something great!
╰─────╯

 astro   v2.1.3 Launch sequence initiated.

   dir   Where should we create your new project?
         ./tutrial

  tmpl   How would you like to start your new project?
         Include sample files
      ✔  Template copied

  deps   Install dependencies?
         Yes
      ✔  Dependencies installed

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict
      ✔  TypeScript customized

   git   Initialize a new git repository?
         Yes
      ✔  Git initialized

  next   Liftoff confirmed. Explore your project!

         Enter your project directory using cd ./tutrial 
         Run npm run dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯

表示してみる

tutrial ディレクトリに移動し、npm run dev で表示してみます。host オプションで 0.0.0.0 を指定してください。オプションを設定しないと Nginx がエラーをはきます。

参考サイト github.com

$ cd tutrial
$ npm run dev -- --host 0.0.0.0

以下のような表示が出てくれば OK です。

Astro

OpenAI API を GitHub Codespaces の Node.js から呼び出してみる

概要

[ChatGPT] Open AI API を Node.js x TypeScript で試してみたという面白そうな記事が、DevelopersIO にアップされていました。今回は、これにアレンジを加えて試してみます。

具体的には、セットアップやコーディングの作業をローカルマシンではなく、GitHub Codespaces で行います。

また、OpenAI API キーを GitHub に晒すのが嫌なので、外部から読み込みできない Codespaces の secret に環境変数として置いて、js で読み込めるようにしてみます。

解説動画


www.youtube.com

この記事の目次

GitHub リポジトリ作成

わかりやすい名前で、GitHubリポジトリを作成します。今回は、とりあえず openai_api_node という名前でリポジトリを作成しました。Add a README file や Add .gitignore については、必要であれば、適宜追加してください。(今回は、Add .gitignore のプルダウンのところで、Node を指定し、.gitignore を追加しておきました。一応、README も追加しておきました。)

codespace 作成

作成した GitHub リポジトリの <>Code▼ ボタンをクリックします。Codespaces のタブにある Create codespace on main ボタンをクリックします。

オプションを使用した詳細設定については、リポジトリの codespace を作成する が参考になります。

Node.js と Typescript 設定

まずはバージョンを確認してみます。

$ node -v
v19.7.0

次に package.json を作成、Typescript 導入、自動コンパイル設定などを行います。

詳細設定については、Node.js & TypeScriptのプロジェクト作成 が参考になります。

$ npm init -y
$ npm install typescript @types/node ts-node nodemon --save-dev
$ npx tsc --init --rootDir src --outDir lib --esModuleInterop --resolveJsonModule --lib es6,dom --module commonjs

package.json 変更

package.json の scripts を以下に変更します。

"scripts": {
"start": "npm run build:live",
"build": "tsc -p .",
"build:live": "nodemon --watch 'src/**/*.ts' --exec \"ts-node\" src/index.ts"
},

OpenAI API Key 作成

OpenAI のアカウントにログイン後、以下の URLにアクセスして API Key を作成します。

Account API Keys - OpenAI API

できたキーをコピーして、どこかに保存しておいてください。次の GitHub Secret 作成で使用します。

GitHub Secret 作成

作成したリポジトリで、

Settings > Secrets and variables > Codespaces

と進んでいき New repository secret というボタンをクリックして、secret を作成します。

名前は適当なもので結構です。先ほど、コピーしておいた OpenAI API Key を該当箇所にペーストしたら secret の準備は OK です。

openai モジュール追加

Node.js に openai モジュールを追加します。

$ npm install openai

index.ts 作成

まず、src ディレクトリを作成し、その配下に index.ts を作成します。

import { Configuration, OpenAIApi } from "openai";

const configuration = new Configuration({
    apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

export async function ask(content: string, model = "gpt-3.5-turbo-0301") {
  const response = await openai.createChatCompletion({
    model: model,
    messages: [{ role: "user", content: content }],
  });

  const answer = response.data.choices[0].message?.content;
  console.log(answer);
}

const question = "wowakaさんについて教えてください";
ask(question);

実行

$ npm start

結果がかえってきて成功ですが、内容的には不正確なものでした。こういった面もあるのが、OpenAI だということを認識できてよかったと思います。

OpenAI API

MUI 公式 Example の改造 その 4 -- JEST & React Testing Library

概要

MUI 公式 Example の中から Next.js (TypeScript version) を雛形とし、自分の作りたいサイトに改造する 4 回目です。 今回は、JEST と React Testing Library をインストールし、最後のほうで簡単なテストのサンプルを使って練習をしてみます。

Rails を使っていた頃は、少しはテストをしていたのですが、なんか面倒で積極的にやりたい気が起きませんでした。 テストに対する苦手意識を克服するためにも、なんとか頑張ってみたいと思います。

数回に分けて連載する MUI に関連する記事の全体像

この記事の目次

ホスト

Mac

package.json

参考サイト

Jestの設定 · Jest

この頃流行りのJestを導入して軽快にJSをテストしよう - Qiita

% vim package.json

以下を追加

"scripts": {
  "test": "jest",
  "test:ci": "jest --ci"
},



"devDependencies": {
  "@testing-library/jest-dom": "5.14.1",
  "@testing-library/react": "12.0.0",
  "@testing-library/user-event": "13.2.1",
  "@types/node": "^17.0.0",
  "babel-jest": "27.0.6",
  "eslint-plugin-testing-library": "4.11.0",
  "jest": "27.3.1",
  "react-test-renderer": "17.0.2"
}

インストール

% npm install

jest.config.js

参考サイト

next.js/jest.config.js at canary · vercel/next.js · GitHub

設定

基本的に Vercel の上記公式サイトのものを使用していますが、"testEnvironment": "jsdom", の部分だけ追加しました。デフォルトは、node なのですが、テストによってはエラーが出るので、jsdom にしております。詳細については、以下のサイトが参考になりました。

Jestの「 The error below may be caused by using the wrong test environment」の解決方法 - Qiita

% vim jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  "testEnvironment": "jsdom",
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

jest.setup.js

参考サイト

next.js/jest.setup.js at canary · vercel/next.js · GitHub

設定

Vercel のものを、そのまんま使いました。

% vim jest.setup.js
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

tests ディレクトリ作成

% pwd
/Users/ユーザー/React/school-test
% mkdir __tests__

practice ディレクトリ作成

練習用のファイルをひとまとめに保管するためのディレクトリを準備しました。

% pwd
/Users/ユーザー/React/school-test/src/pages
% mkdir __practice__

テスト例 1 ( CommonJs )

まずは、CommonJs を使った場合のテストを練習してみます。実際のプログラムで、CommonJs を使うことは、今後、あまりないとは思いますが、ES6 との違いがわかるので、やってみて損はないのではないでしょうか。

参考サイト

はじめましょう · Jest

commonjs_sum.js

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/commonjs_sum.js
# CommonJs
function sum(a, b) {
  return a + b;
}
module.exports = sum;

commonjs_sum.test.js

vim /Users/ユーザー/React/school-test/__tests__/commonjs_sum.test.js
const sum = require('../src/pages/__practice__/commonjs_sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

実行

% npm test commonjs_sum.test.js

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "commonjs_sum.test.js"

 PASS  __tests__/commonjs_sum.test.js
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.671 s
Ran all test suites matching /commonjs_sum.test.js/i.

テスト例 2 ( ES6 無名関数 )

次は、TypeScript です。ES6 の無名関数の場合のテストを練習してみます。プログラムの中身が、無名関数であろうが、アロー関数であろうが、テストの書き方は同じで大丈夫です。

参考サイト

JestをES6化してimport/exportを使う方法

noname_sum.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/noname_sum.ts
// ES6 無名関数
const sum = function(a:number, b:number):number {
  return a + b;
}

export default sum

noname_sum.test.ts

vim /Users/ユーザー/React/school-test/__tests__/noname_sum.test.ts
import sum from '../src/pages/__practice__/noname_sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

実行

% npm test noname_sum.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "noname_sum.test.ts"

 PASS  __tests__/noname_sum.test.ts (6.735 s)
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.892 s
Ran all test suites matching /noname_sum.test.ts/i.

テスト例 3( ES6 アロー関数 )

次は、ES6 のアロー関数の場合です。テストの書き方は、上記の無名関数のときと全く同じです。あたりまえといえば、あたりまえですが。

参考サイト

JestをES6化してimport/exportを使う方法

FN1609010 | TypeScript入門 09: アロー関数式 | HTML5 : テクニカルノート

arrow_sum.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/arrow_sum.ts
// ES6 アロー関数
const sum = (a:number, b:number) => a + b;

export default sum

arrow_sum.test.ts

vim /Users/ユーザー/React/school-test/__tests__/arrow_sum.test.ts
import sum from '../src/pages/__practice__/arrow_sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

実行

% npm test arrow_sum.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "arrow_sum.test.ts"

 PASS  __tests__/arrow_sum.test.ts (6.404 s)
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.566 s
Ran all test suites matching /arrow_sum.test.ts/i.

テスト例 4

スプレッド構文や reduce を使っているサンプル。自分用のメモとして、ここに上げておきます。

参考サイト

Jest - TypeScript Deep Dive 日本語版

スプレッド構文 - JavaScript | MDN

【JavaScript】”…”ドットが3つ…?これ、どうやって使うの?スプレッド構文の挙動と活用方法|ProgLearn|エンジニアのためのプログラミング情報メディア

【javascript】reduce - Qiita

JavaScript reduce関数の基本的な使い方

foo.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/foo.ts
export const sum
  = (...a: number[]) =>
    a.reduce((acc, val) => acc + val, 0);

foo.test.ts

vim /Users/ユーザー/React/school-test/__tests__/foo.test.ts
import { sum } from '../src/pages/__practice__/foo';

test('basic', () => {
  expect(sum()).toBe(0);
});

test('basic again', () => {
  expect(sum(1, 2)).toBe(3);
});

実行

% npm test foo.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "foo.test.ts"

 PASS  __tests__/foo.test.ts (6.44 s)
  ✓ basic (3 ms)
  ✓ basic again

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        6.59 s
Ran all test suites matching /foo.test.ts/i.

テスト例 5

String を使ったサンプル。自分用のメモとして。

参考サイト

TypeScript のテストを Jest (ts-jest) でやってみる - Qiita

greet.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/greet.ts
export default (name: string): string => `Hello, ${name}!`;

greet.test.ts

vim /Users/ユーザー/React/school-test/__tests__/greet.test.ts
import greet from '../src/pages/__practice__/greet'

describe('greet', (): void => {
  test('should say hello to Tom.', (): void => {
    const response: string = greet('Tom');
    expect(response).toBe('Hello, Tom!');
  });
})

実行

% npm test greet.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "greet.test.ts"

 PASS  __tests__/greet.test.ts (6.407 s)
  greet
    ✓ should say hello to Tom. (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.554 s
Ran all test suites matching /greet.test.ts/i.

テスト例 6

参考サイト

React Testing Libraryの使い方 - Qiita

App.js

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/App.js
import React from 'react';

const title = 'Hello React';

function App() {
  return <div>{title}</div>;
}

export default App;

App.test.js

vim /Users/ユーザー/React/school-test/__tests__/App.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';

import App from '../src/pages/__practice__/App';

describe('App', () => {
  test('renders App component', () => {
    render(<App />);

    screen.debug();
  });
});

実行

% npm test App.test.js

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "App.test.js"

"next/jest" is currently experimental. https://nextjs.org/docs/messages/experimental-jest-transformer
  console.log
    <body>
      <div>
        <div>
          Hello React
        </div>
      </div>
    </body>
    
    /Users/ユーザー/React/school-test/__tests__/App.test.js:15:24
      13 |

      at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:89:13)

 PASS  __tests__/App.test.js
  App
    ✓ renders App component (80 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.142 s, estimated 3 s
Ran all test suites matching /App.test.js/i.

まだまだ練習したりないので、続きは後日。