Closed74

shetommy.comのver1.0.0のやり残しを潰していく

蔀

まずはアラートに対応する。Next.jsのバージョンが10で、11が出てるのでアップデートしろ、的な内容のようだ

蔀
$ npm install react@latest react-dom@latest
+ react@17.0.2
+ react-dom@17.0.2
updated 2 packages and audited 500 packages in 3.493s
found 1 moderate severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details

Reactのアプデの後で脆弱性があると言われたが、そのままNext.jsのアプデしたら解消した

蔀

npmの使い方がよくわかってないので、ついpackage.jsonを手書きで編集してバージョン上げようとしてしまうのだが、普通はnpm経由で行う模様

$ npm outdated 
Package                           Current   Wanted   Latest  Location
@notionhq/client                    0.2.1    0.2.4    0.4.1  shetommy-com
@types/react                      17.0.11  17.0.11  17.0.27  shetommy-com
@typescript-eslint/eslint-plugin   4.27.0   4.33.0   4.33.0  shetommy-com
@typescript-eslint/parser          4.27.0   4.33.0   4.33.0  shetommy-com
eslint                             7.28.0   7.32.0   7.32.0  shetommy-com
eslint-plugin-import               2.23.4   2.24.2   2.24.2  shetommy-com
eslint-plugin-react                7.24.0   7.26.1   7.26.1  shetommy-com
prettier                            2.3.1    2.4.1    2.4.1  shetommy-com
typescript                          4.3.2    4.4.3    4.4.3  shetommy-com

Next.js、Notion APIだけ上げればと思っていたけど、地味にバージョン遅れてるな

蔀

パッケージ更新は二通り手段があるみたい

  1. package.jsonを更新して、再install
  2. ncuを使う
蔀

更新してみた

$ sudo npm install -g npm-check-updates
$ ncu -u
Upgrading /Users/xxx/Dev/shetommy-com/package.json
[====================] 16/16 100%

 @types/react                      17.0.11  →  17.0.27     
 @typescript-eslint/eslint-plugin  ^4.27.0  →  ^4.33.0     
 @typescript-eslint/parser         ^4.27.0  →  ^4.33.0     
 eslint                            ^7.28.0  →  ^7.32.0     
 eslint-plugin-import              ^2.23.4  →  ^2.24.2     
 eslint-plugin-react               ^7.24.0  →  ^7.26.1     
 prettier                           ^2.3.1  →   ^2.4.1     
 typescript                         ^4.3.2  →   ^4.4.3     
 @notionhq/client                   ^0.2.1  →   ^0.4.1     

Run npm install to install new versions.
蔀

とりあえず画像対応を後回しで、Notion APIのバージョンだけ上げようとしたら、日付プロパティが取れなくなった?!

const publishDateObject = post.properties['publish date']
const dateString =
  publishDateObject == undefined
    ? post.last_edited_time
    : publishDateObject.date.start
const date = new Date(dateString).toLocaleDateString()
蔀

@notionhq/clientを"version": "0.2.1"→ "version": "0.4.1"にアップデートした

蔀

TypeScriptのバージョンアップのせいなのかな?
よくわからない

蔀

とりあえずNotion側でプロパティ名にアンダースコア入れて対応したらビルドできた

const publishDateObject = post.properties.publish_date

うーん……

蔀

いやでもnullはnullだった。
仕様的にpublish dateがnullだと、作成日付を入れる仕様にしてたから、パッと見は上手くいってるように見えてしまった

データ構造を整理すると、

Post
{
  object: 'page',
  id: 'xxx',
  created_time: '2021-07-08T22:49:00.000Z',
  last_edited_time: '2021-07-09T07:06:00.000Z',
  cover: null,
  icon: null,
  parent: {
    type: 'database_id',
    database_id: 'xxx'
  },
  archived: false,
  properties: {
    publish_date: { id: 'xL%5EM', type: 'date', date: [Object] },
    Name: { id: 'title', type: 'title', title: [Array] }
  },
  url: xxx
}
publish_date
{
  id: 'xL%5EM',
  type: 'date',
  date: { start: '2010-05-29', end: null }
}
蔀

これ前も確か同じことでTypeScriptの型ワーニング解消できなかった気がする。。。
とりあえずバージョンを戻して、Next.jsだけ上げる

蔀

TypeScriptで解消できない型の問題

  • propsの型指定が上手くいかない
  • オブジェクトのプロパティのアクセス指定が上手くいかない
  • Notionで定義したプロパティに型がないので受けられない
  • (Notion APIの型定義が足りないのか、俺が何かInterfaceつくらないといけないのかもよくわからない)

一週間ぐらいじっくりTypeScriptの勉強しないといけないのを感じている

蔀

全戻ししようかと思ってたけど、冷静に考えるとNotion API以外のパッケージのアップデートが影響してる気がするので、@notionhq/clientだけのアップデートにしてみる

蔀

いや、そんなことはなかった。
@notionhq/clientだけアップデートしても同じようにDateが取れなくなる……

蔀

とりあえずNext.jsのバージョンあげたが、GithubのDependabot alertsが解消されなかった

https://github.com/0si43/shetommy.com/security/dependabot

というかむしろ増えた。
そもそもyarn.lockは使ってなかったので、リネームして退避した
でもワーニングが消えなかったので、dismiss扱いにした

蔀

とりあえずGAを入れるところからやろう

蔀

↑このサイトの通りにやったつもりだったが、

TypeError: Cannot read properties of null (reading 'tagName')

となった。うーん……?

蔀

一晩たってよく読んだら、HeaderコンポーネントにGoogleAnalytics入れてたのが、要らなかったことに気づいた

蔀

このワーニングは、GAとは別件だった。

react-dom.development.js?61bb:67 Warning: validateDOMNesting(...): <tr> cannot appear as a child of <table>. Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by the browser.

https://teratail.com/questions/282907

単に<tbody>がない、と言われているだけだった

蔀
蔀

うっとうしいから手でpackage-lock.json書き換えようかとも思ったけど、eslintのアプデをとりあえず試す

蔀

うーんeslint上げてもまだ一個5.0.0に依存してるのが残っている……

蔀

ncu -uでパッケージ全部最新版にしても残ったので、手で書き換えかライブラリのアプデ待つかの二択っぽい。待ちで行く

蔀

OGP設定、こちらのissueに色々書きこんでる

https://github.com/0si43/shetommy.com/issues/8

蔀

時間があったので、足元を固めるつもりで、JSとTSのWeb教材を読みなおした

https://jsprimer.net/

https://book.yyts.org/

https://typescript-jp.gitbook.io/deep-dive/

なんとなくで進める限界を感じていたので、改めてゆっくり学習しなおすにはいいタイミングだったと思う

蔀

ただNotion APIから返されたデータに対する型づけがまだ俺の中で解決せず

https://github.com/0si43/shetommy.com/issues/2

API仕様の理解はほぼほぼできたと思う

  • Promiseを返す
  • APIのリポジトリのREADME読むと、エラーハンドリングもそこそこ細かくできることがわかった
  • 課題は二点
    • (Notionの)プロパティの扱い。NotionはユーザーがDBの項目を自由に名づけて、データ型も設定することができるので、そこをTypeScriptとしてどう扱うか
      • そういうのはもうAnyが楽なんじゃないか……ともすこし思っている
      • DBのフォーマットは自分でつくったものなので、ソース中との整合性を取ればいいだけなんだろうけど、そこがよくわからない
    • propsの型づけ
      • 単純な例ならいくつか見つけたんだけども、今回はAPIの戻り値の型を指定しなければならなくて、これがよくわからない

基礎から固め直すアプローチはよかったと思う。前進してる感じはある

蔀

DBにあるページをpublished dateでfilter()して、その後sort()すればいい気がしてきた

蔀

個人PCに開発環境なかったので入れる

蔀

色々やった……めんどくさいのでhistoryだけ

  337  brew install node
  338  node -v
  339  xcode-select --install
  340  brew install --build-from-source python@3.9
  341  sudo brew install --build-from-source python@3.9
  342  brew doctor
  343  sudo brew install --build-from-source python@3.9
  344  brew install --build-from-source python@3.9
  345  brew link python@3.9
  346  brew install node
  353  npm install next react react-dom\n
  354  npm audit
  355  npm audit fix
  356  npm run dev
蔀

.env.localを再作成した。
中身を控えておくの忘れて、いざつくりなおそうとしたら結構難しかった。
Vercelに本番環境向けに設定していたのを思い出して、そこからコピーした。

npm run devでlocalhostで実行できることを確認した

蔀

nodeのバージョンが全然違ったり、regexのワーニングが修正できたりしたので、こっちを本番にするかな

蔀

今までssh接続でgit pushしてたけど、今回からhttpsでやることにした

https://qiita.com/shunsa10/items/e43564cf48f84b95455b

手順はこちら。
自分用にまとめると、

  • (自分のMacBookのKeyChainからGithubのキーは削除しておく)
  • GithubのUser > Setting > Developer settings > Personal access tokensでトークンを発行
  • push時にユーザー名、パスワードが要求されるので、↑のトークンをパスワードとして利用
  • Success!

という流れ。今後は簡単なのでこっちにしようと思う

蔀

なぜかnodeのバージョンが全然違う……
昔の会社

brew -v
Homebrew 3.2.16

node -v
v16.11.1

npm -v 
8.0.0

npm view react version
17.0.2

https://zenn.dev/link/comments/8f30fa52d9b159

調べたら、Node.jsの最新LTSはもう16以降になっていた

https://www.publickey1.jp/blog/21/nodejsltsnodejs_16apple_m1javascriptv8_90.html

14でもサポートはしているみたいなので、商用で安定性重視ならアリかもしれないが、まあ俺の個人サイトで16にしない理由はないだろう

蔀

今日は実装イメージついてるところをやってこうかなと思います

蔀

ESlintとPrittierが何もしてないので効かなかった。

  • VSCodeの拡張からESlintとPrittier
  • onSaveでフォーマットするように設定
    https://qiita.com/KKKKK/items/3b9dba55653cb7993bf5
  • VSCode右下の通知欄に出てたダイアログで、「フォーマッタが二種類あるがどちらを選ぶ?」みたいなのが出るので、Prittierを選んだ
  • 任意のファイルにlet text = "Hello, world";を入れて保存して、セミコロンが消えて、シングルクオーテーションになることを確認
  • ↑ここまでやっても、ESLintのワーニングが出てくれなかった
蔀

setting.jsonを指定してやらないと効かなかったみたい
let text = 'Hello, world'のtextにconstにしろワーニングが出たのを確認
ちなみに下線引かれないんだけど、これはそういうものだっけ?

https://zenn.dev/link/comments/3b29e5dd9ed993

蔀

Gitで、こんなワーニングが出た。

git pull
hint: Pulling without specifying how to reconcile divergent branches is
hint: discouraged. You can squelch this message by running one of the following
hint: commands sometime before your next pull:
hint: 
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: 
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.

よく理解できていないが、

https://blog.agile.esm.co.jp/entry/git-warns-pulling-without-specifying-how-to-reconcile-divergent-branches

↑を読んで、pull.rebase falseを設定した

蔀

{ x, y }: Typeで、Type.xとType.yが取れるのか。
つまり下記のコードが同じ意味。やっと理解できた

type Props = InferGetStaticPropsType<typeof getStaticProps>

export default function Post(props: Props) { 
  const page = props.page
  const blocks = props.blocks
}

export default function Post({page, blocks}: Props) { }
蔀

開発環境だとAny撲滅できたと思って、pushしたらVercelでガンガンコケるのムカつくな……
ローカルでもstrict指定みたいなんするといいのかな?

catnosecatnose

可能ならstrict: trueにすると良いと思います!もしくはローカルでnpm run tsc --noEmitを実行するとVercelにpushしなくても型エラーを検証できるかもです。

蔀

ありがとうございます!
tsconfig.jsonはstrict: trueなので、何かNext.js側のローカルビルド設定が緩くなってるのかもです……

僕の環境だとnpx tsc --noEmitで検証できました
これでVercelにムダ打ちしなくてすみそうです!

蔀

型ガードがうまく書けない

https://zenn.dev/nekoniki/articles/d40acfd3c26583

↑ここを参考にして、こんなのを書いてみた

declare type PropertyType =
  | 'number'
  | 'title'
  | 'rich_text'
  | 'url'
  | 'select'
  | 'multi_select'
  | 'people'
  | 'email'
  | 'phone_number'
  | 'date'
  | 'files'
  | 'checkbox'
  | 'formula'
  | 'relation'
  | 'rollup'
  | 'created_time'
  | 'created_by'
  | 'last_edited_time'
  | 'last_edited_by'

declare type TitlePropertyType = 'title'

export const isTitle = (type: PropertyType): type is TitlePropertyType => {
  return type === 'title'
}

これで、

const title = isTitle(titleProperty.type) ? titleProperty.title : ''

こんな感じで使いたかったんだけど、絞りこみが効いてなかった……

蔀
declare type temp = QueryDatabaseResponse['results']

これで一応プロパティの存在チェックもしてくれる模様

蔀

これでいけそう

declare type NotionProperty =
  QueryDatabaseResponse['results'][number]['properties']

export const getTitle = (property: NotionProperty) => {
  return property.Name.type == 'title' ? property.Name.title[0].plain_text : ''
}
蔀

getStaticPropsのparamsがnullableになっちゃうのが気になってたんだけど、インターフェイスを指定することで読めるようになるっぽい。
というかよく読んだらidも渡せそうだし、後続処理で必要なページのパラメーターはタイトルとidだけだった

interface IParams extends ParsedUrlQuery {
  title: string
  id: string
}

export const getStaticProps = async (context: GetStaticPropsContext) => {
  const page = context.params?.page

https://wallis.dev/blog/nextjs-getstaticprops-and-getstaticpaths-with-typescript

蔀

ファイルなんかやなんだよなー
メモリ上で完結させたい……

蔀

これだとreturn省略できて、

const page = database.find((page) => getPageTitle(page.properties) == title)

これだとreturn必須になる(アロー関数の戻り値に{ }をつける)の、よく間違えて、{ }アリでreturn省略してしまう

const page = database.find((page) => { return getPageTitle(page.properties) == title}) 
蔀
if (block.has_children && !block[block.type].children) {

この.childrenってプロパティ、なくなった?

蔀

リテラル型でずっと苦しんでいる

蔀

この分割代入、リテラル型でネストされてるからこれだと取れないと思ってたけど、なんか取れるな。なんでだろ……

const elements = richTexts.map((value, index) => {
    const {
      annotations: { bold, code, color, italic, strikethrough, underline },
      text,
    } = value
蔀
console.log(Object.keys(value))
wait  - compiling...
event - compiled successfully
[ 'type', 'text', 'annotations', 'plain_text', 'href' ]

?!
想定と違うものが入ってる……
ListBlockChildrenResponse['results']じゃないのか

このスクラップは2021/10/24にクローズされました