Open99

eetann's til

eetann / えーたんeetann / えーたん

Python の 判定文

当たり前だけどNoneが入る可能性のある文では

if hoge is not None:
    print("if hoge is not None:")

のようにisis notを使って判定する。

また、if 0Falseになることを忘れてはいけない。

eetann / えーたんeetann / えーたん

JavaScriptの正規表現のメタ文字エスケープ

JavaScriptの正規表現のメタ文字エスケープを一括で行うには、以下の関数でOK

function escapeRegExp(string) {
  return string.replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&');
}

$&はマッチした部分文字列全体を表す。
参考

eetann / えーたんeetann / えーたん

ロジスティック回帰

配列のshapeを変更

以下のような警告が表示された。1次元のデータ(例(50,))が期待されているときに、行や列のベクトルになってしまっている(例(50,1))。

DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel(). y = column_or_1d(y, warn=True)

以下のようにデータそのものを1次元に直せば良い。

y = y.values.ravel()

反復回数の指定

以下のような警告が表示された。要するに、指定した反復回数内では収束しなかったらしい。

ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

以下のようにmax_iterを引数で指定してやる。
max_iterはデフォルトでは100と指定されている。

model = LogisticRegression(max_iter=2000)
model.fit(X_train, y_train)
eetann / えーたんeetann / えーたん

Pandasの指定行の型変換

指定した行に対して、numpyと同じようにastypeを使う

df['i'].astype(str)
eetann / えーたんeetann / えーたん

skimage.transform.resize

skimage.transform.resizeを使うと、リサイズされた返り値のnumpyの型はfloatになってしまう。
元の型を維持したい場合は、引数preserve_range=Trueを指定する。

image = skimage.transform.resize(image, (height, width), preserve_range=True)

なお、次元(RGB方向)の指定はしなくても同じ数で埋めてくれる。

公式ドキュメント:skimage.transform.resize

eetann / えーたんeetann / えーたん

Vimの挿入モードのCTRL-Hが効かなかった問題

試したこと

  1. ターミナルのせいだと思って別のターミナルに切り替え
  2. tmuxのせいだと思ってtmuxではなく普通に起動
  3. vimのせいだと思ってぐぐった(当たり)

:set backspace?で確認したら空だったので以下を実行したら直った。

set backspace=indent,eol,start

参考:Vimメモ : 挿入モードでバックスペースが効かない もた日記

eetann / えーたんeetann / えーたん

環境情報確認コマンド

npx envinfo

実行例

  System:
    OS: Linux 4.19 Ubuntu 20.04.1 LTS (Focal Fossa)
    CPU: (8) x64 Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
    Memory: 4.29 GB / 6.11 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 12.18.3 - ~/.anyenv/envs/nodenv/versions/12.18.3/bin/node
    Yarn: 1.22.5 - ~/.yarn/bin/yarn
    npm: 6.14.10 - ~/.anyenv/envs/nodenv/versions/12.18.3/bin/npm
  Managers:
    Apt: 2.0.2 - /usr/bin/apt
    pip3: 20.0.2 - /usr/bin/pip3
    RubyGems: 3.1.2 - /usr/bin/gem
  Utilities:
    Make: 4.2.1 - /usr/bin/make
    GCC: 9.3.0 - /usr/bin/gcc
    Git: 2.25.1 - /usr/bin/git
  IDEs:
    Nano: 4.8 - /usr/bin/nano
    Vim: 8.2 - /usr/local/bin/vim
  Languages:
    Bash: 5.0.17 - /usr/bin/bash
    Go: 1.13.8 - /usr/bin/go
    Perl: 5.32.0 - /home/linuxbrew/.linuxbrew/bin/perl
    Python: 3.8.5 - /usr/bin/python
    Python3: 3.8.5 - /usr/bin/python3
    Ruby: 2.7.0 - /usr/bin/ruby

https://github.com/tabrindle/envinfo

プログラム実行環境のメモだけではなく、プログラムそのものにも使えるそう。
もう少し踏み込んで使い始めたら記事にするかも。

eetann / えーたんeetann / えーたん

Vimの起動が遅くなった(WSL2)

vim --startuptime startuptime.txt

で確かめたら、

069.149  000.070: loading after plugins
069.157  000.008: inits 3
069.260  000.103: reading viminfo
32414.768  32345.508: setup clipboard
32414.850  000.082: setting raw mode
32414.864  000.014: start termcap

クリップボードが怪しいらしい。

VcXsrcのアイコンにカーソルを持ってきたときの表示がCygwin/X なんちゃらみたいにいつもと違った。
VcXsrcの設定いろいろいじったけど、結局はVcXsrcのアンスト&インストでもとに戻って、Vimの起動速度も元通り。

eetann / えーたんeetann / えーたん

Vim で特定の文字に挟まれている部分を選択する

ずっと使ってるmachakann/vim-sandwichにこの機能があることを忘れていたのでメモ。
<Plug>(textobj-sandwich-query-i)is
<Plug>(textobj-sandwich-query-a)as
に割り当てられている。

たとえば、以下で1にカーソルがあってノーマルモードの場合、

hoge $\frac{1}{3}$ fuga

vis$と入力すれば、
選択モードになって(v)、
囲み文字入力待機状態になって(is)、
囲み文字を指定すると($)、
\frac{1}{3}が選択された状態になる。

vas$だと、囲み文字も入るので$\frac{1}{3}$が選択される。

eetann / えーたんeetann / えーたん

Figma でフォントスタイルが適用できないとき

フォントスタイルのフォントが使えないものだと適用できない。その場合はフォントを入れ替える。警告が出ているからわかると思う。

eetann / えーたんeetann / えーたん

UbuntuでSuper-pの入力でディスプレイ設定が戻ってしまう問題

sudo apt install dconf-editor
dconf-editor

dconf-editor の
/org/gnome/mutter/keybindings/switch-monitor
デフォルト設定を無効にして、['<Super>p', 'XF86Display'][]に変更
もし/org/gnome/settings-daemon/plugins/media-keys/video-outもあったら同様にカスタマイズ

参考
gnome - How to disable global Super-p shortcut? - Ask Ubuntu

eetann / えーたんeetann / えーたん

React Toutesのチュートリアルをやる

https://reactrouter.com/docs/en/v6/getting-started/tutorial

Nest

URL遷移時に今のページの指定した場所に表示させるなら、Routeをネストし、親側にOutletを追加する。

URLが定義されていないとき

path="*"で設定できる。

URLが固定じゃないとき

/foo/hogeのように固定ではなく、/foo/001 /foo/092のように渡したパラメータによって変えたいときは、
path="hoge"ではなく、path=":hoge"とする。
パラメータ自体は、そのパラメータを使ったコンポーネントでlet params = useParams()&params.hogeのように取得する。

↑のパラメータを選択していない状態のとき=その親ルート に表示させたい内容は、同階層にindexの属性をつけたRouteを書いておく。

eetann / えーたんeetann / えーたん

どのリンクがアクティブなのか

LinkではなくNavLinkを使い、style属性またはclassName属性の引数としてisActiveを使って色とかを変える。

URLのSearch paramsとして扱う

let [searchParams, setSearchParams] = useSearchParams();のようにして設定する。searchParams.get("hoge")?hoge=の値を取得できる。
setSearchParamsonChangeなどに書いておけば、入力した時点で、URLのパラメータを随時変化させてくれる。

ただし、そのままだと遷移時に消えてしまう。そこで、NavLinkのラッパーとなるコンポーネントを作成し、let location = useLocation();&location.searchを使うことで、遷移時にもURLのパラメータが維持できる。

let isActive = params.getAll("brand").includes(brand);
のようにすれば可能。

eetann / えーたんeetann / えーたん

複数選択のパラメータにしたい時

searchParamsappendメソッドや、filterを使って新しい URLSearchParamsを代入する。

自動で遷移させたい時

リンククリックなどの手動ではなく、ボタンクリック時に処理をしてから遷移させたいときにはuseNavigateを使う。

eetann / えーたんeetann / えーたん

Chrome拡張機能

v3

service worker

service workerとmanifest.jsonは同じディレクトリに置く。

The script used for "service_worker" must be located in your extension's root directory.

Manage events with service workers - Chrome Developers

現在のタブについて情報を取得

backgroundだとchrome.tabs.getCurrentでは取得できないため、 chrome.tabs.queryを使う。

Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example, a background page or popup view).

eetann / えーたんeetann / えーたん

Redux Tutorial

このメモは途中から。

https://redux.js.org/tutorials/essentials/part-4-using-data

  • Redux storeを使えばいろんなコンポーネントからデータにアクセスできる
  • ただし、すべてのデータをReduxに管理させなくても良い
  • 必要に応じてReactのstateと使い分けるべき
  • reducerの中ではidの生成のような予測できないランダムなことはしない。そのかわり、 prepareが使える
eetann / えーたんeetann / えーたん

https://redux.js.org/tutorials/essentials/part-5-async-logic

selectorロジックの使いまわし

useSelectorの中で特定のデータがほしいときに書いているstate => state.hogeが何回も使うロジックになってきたら、createSliceを書いているファイルにselectAllHoge,selectHogeByIdのような関数として書いておくと便利。

リクエストの状態について

状態をisLoading: trueみたいに書くこともできるけど、どうせ4つの状態が存在するならはじめから以下のように定義しておくと良いかも。

{
  // Multiple possible status enum values
  status: 'idle' | 'loading' | 'succeeded' | 'failed',
  error: string | null
}

createAsyncThunk

第一引数はaction typeのprefixになる。
第二引数は実際にAPIのデータをとってくる処理を書く。

eetann / えーたんeetann / えーたん

extraReducers

reducerに定義していないcreateAsyncThunkのようなものを書いておく???
createAsyncThunkのリクエストが〇〇になったら、Aする。みたいな???

reducers と extraReducers の違い

reducersは "action creater"を作り、アクションに対して応答するもの。
extraReducersは 応答だけするもの。 createAsyncThunk自体が"action creator"を使っているっぽい。

reactjs - What is difference between reducers and extrareducers in redux toolkit? - Stack Overflow

eetann / えーたんeetann / えーたん

Redux Tutorial Part6

https://redux.js.org/tutorials/essentials/part-6-performance-normalization

userページで何も表示されない

最初、以下のように書いていた。

export const selectUserById = (state, userId) => {
  state.users.find((user) => user.id === userId)
}

これだと、何も返してないことになるので、括弧無しで一行にしたり、returnつけたり、括弧は外したりしてちゃんと返す。

export const selectUserById = (state, userId) =>
  state.users.find((user) => user.id === userId)
eetann / えーたんeetann / えーたん

notificationページで何も表示されない

エラー内容は以下。

Uncaught TypeError: Cannot read properties of undefined (reading 'map')

単純にconfigureStoreにreducerを登録していなかっただけだった。

useLayoutEffect

useEffectは一回描画してから実行されるけど、useLayoutEffectは描画される前に実行される。

classnames

classNameで複雑になってきたら使う。

eetann / えーたんeetann / えーたん

React DevTools Profiler

以下の手順でどこが再レンダリングされたのかがわかる。

  1. Start profilingを押す
  2. クリックとかする
  3. Stop profilingを押す

メモ化

Reactの用語かと思ったら違った。

以下はメモ化 - Wikipediaから引用。

メモ化された関数は、以前の呼び出しの際の結果をそのときの引数と共に記憶しておき、後で同じ引数で呼び出されたとき、計算せずにその格納されている結果を返す

一回実行したらそれをメモしておいて計算回数が減らせる。

たとえば、useSelectorの中でhogeArray.filter`を返すと、Array.prototype.filter()は毎回新しい配列を返すので、hogeArrayに関わる内容を変更していなくても各actionで再レンダリングが発生してしまう。
これをメモ化で解決できる。

Reselect

https://github.com/reduxjs/reselect

selectorをメモ化するライブラリ。createSelectorを使う。
まず、第一引数はinput selectorと呼ばれるもの。

Reduxのreselectとは - Qiitaより

input selector は「state を引数に受け取り、関心のある部分を返すだけの関数」
だからチュートリアルではAll系が書かれていた。

そして、第二引数はoutput selectorと呼ばれるもの。
「第一引数であるinput selector の返り値」を引数として受け取り、値を返す処理。

reselect - npmに書かれている例を見たら引数の対応がわかりやすかった。

eetann / えーたんeetann / えーたん

分割代入で別名をつける

const {元の変数名: 新しい変数名} = オブジェクト

const o = {p: 42, q: true};
const {p: foo, q: bar} = o;

console.log(foo); // 42
console.log(bar); // true

分割代入 - JavaScript | MDN

Normalized State Structure​

state構成の正規化?
idとかを配列でfindしてループになっちゃうよりも、オブジェクトのキーにしたほうが探すの楽。

  • データを複製しない
  • itemのIDをキー、内容を値とするルックアップテーブルでデータを保つ

createEntityAdapter

createEntityAdapter{ ids: [], entities: {} }の形で実体を操作できる。データの保存先とかは変わらずstateで管理できる。
それ以外の値はgetInitialStateメソッドに渡す。
getSelectorsを分割代入すれば、selectAllなどが使える。ここで、別の変数名を割り当てておかないと、複数のcreateEntitiyAdapterを使うときに面倒になる。
hogeAdapter.upsertMany(state, action.payload)では一括で追加数rだけではなく、存在するitemをidに基づいてマージすることができる。

sliceで正規化したデータを扱うときに便利な関数が詰まったもの。

eetann / えーたんeetann / えーたん

GraphQL

公式ドキュメントだと、手を動かそう、みたいなチュートリアルではないので以下の記事で学ぶ。

https://reffect.co.jp/html/graphql

GraphQLは、過不足なくとってこれるAPIのためのクエリ言語。

一旦公式読む

公式の(Queries and Mutations | GraphQL)読んだ。
クエリと結果の対応がわかりやすい。
task(id: "10000")で単体の取り出せるし、同じフィールドに対して別の引数を渡すときもAliases一緒に書ける。繰り返しになる部分はFragmentを使って二度書かなくていいようになる。

{}じゃなくてquery Hogeで名前をつけられる。前者は、operation typeがqueryであるときの省略記法。
$hogeで変数が書ける。
directiveを使えば条件分岐も可能。

チュートリアルに戻る

スキーマのタイプ

  • query
    • Read Operation
    • 問い合わせ
  • mutation
    • Write Operations
    • 作成、更新、削除
  • subscription
    • リアルタイムのRead Operation?

ID型は一意の文字列。

eetann / えーたんeetann / えーたん

続き

resolversの設定

(DNSにおける)リゾルバ (resolver)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

ApolloServerでは、gql関数で定義したスキーマやどんな値が戻ってくるのかを書くresolversに書く。

以下のように自分で書いたオブジェクトが入れ子になっている場合、

type User {
    id: ID!
    myPosts: [Post]
  }

resolversでもQueryと同じ階層にUser:{ myPosts: hoge}のように書けば良い。この時、myPostsparent引数を使って親Userの結果を取得できる。

Prisma

Prisma - Next-generation Node.js and TypeScript ORM for Databases
ORM: Object Relational Mapping。SQLじゃなくてオブジェクトで操作できるように対応付け。
ORMとは?|SQLAlchemy 概要と基本の使い方
今はPrismaへ寄り道はしない。

eetann / えーたんeetann / えーたん

TablePlus

エレガントなUIでデータベースを管理するツール
https://docs.tableplus.com/
AURにはあるっぽいけど公式のインストール方法に掲載されてないから試すのはやめる。

Prismaの続き

↓で使用感がわかった。

prisma.user.update({
  where: { id: args.id },
  data: { name: args.name },
});

Apolloのメモ

useQueryを使うことで、データだけじゃなくてローディングやエラーもわかる。

https://reffect.co.jp/html/graphql#vanilla_JavaScript

eetann / えーたんeetann / えーたん

宣言的とは

一言だけであとは勝手にやってくれる、ということ?
逆の「命令的」は順番に細かく指示する感じ?

  • 命令的
    • Imperative
    • How
    • 何をするか記述
    • 命令を順番に処理するので、最終的な結果が前回の実行結果に依存する
  • 宣言的
    • Declarative
    • What
    • どんな状態か記述
    • 状態そのものを記述するので、最終的な結果=状態が前回の実行結果に依存しない

参考

eetann / えーたんeetann / えーたん

Apollo GraphQL チュートリアル

https://www.apollographql.com/tutorials/lift-off-part1/

gqlテンプレートリテラルの中では、"で囲った部分や"""で囲って改行を含むdescriptionsが書ける。


ApolloServerにはmocksにモックデータを指定することができる。

eetann / えーたんeetann / えーたん

https://www.apollographql.com/tutorials/lift-off-part2/exploring-our-data

data sources

GraphQLのAPIは複数のデータソースと接続できる。

REST API から取ってくるなら、 RESTDataSourceクラスを継承すれば良い。コンストラクタで baseURLを設定できる。

resolvers

  • parent
    • 親のリゾルバの返り値
  • args
    • GraphQLの引数
    • idとかはまずここから
      • オブジェクトの中にオブジェクトとかなら ここからparentに渡る
  • context
    • authentication情報、データベース接続など
    • 今回のチュートリアルなら RESTDataSource
  • info
    • フィールドネーム、ルートからフィールドまでのパス?
    • そんな使わない
eetann / えーたんeetann / えーたん

https://www.apollographql.com/tutorials/lift-off-part3/resolver-chains

Resolver Chains

分かりやすすぎるwww
parentの流れも分かりやすい
no Resolver Chains

Resolver Chains

Explorer

responseはjsonだけじゃなくて表形式でも見れる。

Variable

client側でクエリの変数は useQueryの引数でvariablesオブジェクトの中に渡す。

const {loading, error, data} = useQuery(GET_TRACK, {
  variables: {trackId}
});

sever側ではschemaのtype Query{}の中でクエリ名のあとの括弧の中に書く。

eetann / えーたんeetann / えーたん

https://www.apollographql.com/tutorials/lift-off-part4/what-is-a-mutation

Mutationのresponseにcode, success, message が含まれることは共通。
resolversではasync, awaitを使ってこれらを含むようにreturnしないとだめ。 resolverで try/catch を使うとエラーハンドリングしてreturnできる。

書く流れ

  • server
    1. schema
    2. data source
    3. resolvers
    4. test @ Apollo Studio Explorer
  • client
    1. query, mutation
    2. useHoge
    3. useQueryはその時点で実行されるけど、useMutationはその返り値である mutate function を実行しないとmutationされない
eetann / えーたんeetann / えーたん

createEntityAdapterのsetAll

setAll : accepts an array of entities or an object in the shape of Record<EntityId, T>, and replaces all existing entities with the values in the array.

setAllsetMany は配列だけではなくオブジェクトでも可。

createEntityAdapter | Redux Toolkit

eetann / えーたんeetann / えーたん

メモ

npm install --save-dev\
  typescript \
  ts-node \
  eslint \
  prettier \
  eslint-config-prettier \
  eslint-import-resolver-typescript \
  eslint-plugin-{import,prettier,react,react-hooks,unused-imports} \
  @trivago/prettier-plugin-sort-imports \
  @typescript-eslint/parser \
  @typescript-eslint/eslint-plugin
eetann / えーたんeetann / えーたん
.eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["react", "@typescript-eslint", "unused-imports", "react-hooks"],
  rules: {
    "react/jsx-uses-vars": 1,
    "react/jsx-uses-react": 1,
    "react/react-in-jsx-scope": "off",
    "@typescript-eslint/no-unused-vars": "off",
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "warn",
      {
        vars: "all",
        varsIgnorePattern: "^_",
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies
  },
  settings: {
    react: {
      version: "detect",
    },
  },
};
eetann / えーたんeetann / えーたん
.prettier.js
module.exports = {
  importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
  importOrderCaseInsensitive: true,
  importOrderSortSpecifiers: true,
  importOrderSeparation: true,
}
eetann / えーたんeetann / えーたん

現在のブランチ名にissue番号があればコミットメッセージに含める

.git/hooks/prepare-commit-msg
#!/bin/sh

# $1 COMMIT_MSG_FILE
# $2 COMMIT_SOURCE --ammend 判定に使用

if [ "$2" == "" ] ; then
  ISSUE_NUM=`git rev-parse --abbrev-ref HEAD | sed -E "s/.*#([0-9]+).*/\1/g"`
  sed -i "1i  #$ISSUE_NUM" $1
fi
$ chmod +x .git/hooks/prepare-commit-msg

参考

eetann / えーたんeetann / えーたん

GitHub Actions

https://blog1.mammb.com/entry/2021/12/11/090000

echo とかのコマンドを実行するだけなら該当するリポジトリの情報はいらないけど、普通にlintとかformatするときはリポジトリの情報が必要になるため、actions/checkout@v3を使う。
https://github.com/actions/checkout

nodeを準備。
https://github.com/actions/setup-node

npm ciを使うほうが良さそう。
https://new-lamp.hatenablog.com/entry/2021/10/23/102630

npmにおけるキャッシュについて
https://github.com/actions/cache/blob/main/examples.md#node---npm

eetann / えーたんeetann / えーたん

Squoosh で画像をリサイズできるが、これはCLIもある。CLIでリサイズするときに指定するオプションは7つぐらい必要。ただし、全部指定しなくてもエラーにはならず、正常終了して何も起こらないので分かりづらい。
オプションを指定するのが面倒な場合は一旦Webで試してCLIコマンドとしてコピーするボタンを押してから実行するといいかも。
CLIだと一括実行とか書きやすい。

eetann / えーたんeetann / えーたん

Chrome Extension icon用

まず、Web版でコピーできるコマンドにファイルしていを加えたもの。

npx @squoosh/cli --resize '{"enabled":true,"width":128,"height":128,"method":"lanczos3","fitMethod":"stretch","premultiply":true,"linearRGB":true}' --oxipng '{"level":2,"interlace":false}' -d hoge --suffix '-128x128' ./foo.png
eetann / えーたんeetann / えーたん

次に、ファイルサイズを変数で指定するもの。
書き換え箇所が1つで済む。
zsh(ZLE?)のedit-command-lineを使うと更に書き換えやすくなる。

size=128;npx @squoosh/cli --resize '{"enabled":true,"width":'$size',"height":'$size',"method":"lanczos3","fitMethod":"stretch","premultiply":true,"linearRGB":true}' --oxipng '{"level":2,"interlace":false}' -d hoge --suffix '-'$size'x'$size ./foo.png
eetann / えーたんeetann / えーたん

改行入れて見やすくしたもの。

size=48;\
npx @squoosh/cli \
  --resize '{"enabled":true,"width":'$size',"height":'$size',"method":"lanczos3","fitMethod":"stretch","premultiply":true,"linearRGB":true}' \
  --oxipng '{"level":2,"interlace":false}' \
  -d hoge \
  --suffix '-'$size'x'$size \
  ./foo.png
eetann / えーたんeetann / えーたん

CRXJS Vite Plugin のmanifestで怒られる

import manifest from './src/manifest.json'で、resolveJsonModuleをtrueにしようねなどと怒られる。

Cannot find module './src/manifest.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.
Module '"src/manifest"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
File 'src/manifest.json' is not listed within the file list of project 'tsconfig.node.json'. Projects must list all files or use an 'include' pattern.

tsconfig.node.jsonに手を加えれば解決。

tsconfig.node.json
{
  "compilerOptions": {
    "composite": true,
    "module": "esnext",
+    "resolveJsonModule": true,
+   "allowSyntheticDefaultImports": true,
    "moduleResolution": "node"
  },
+  "include": ["vite.config.ts", "./src/manifest.json"]
}
eetann / えーたんeetann / えーたん

拡張機能の更新をCLIで行う

chrome-webstore-upload-cliを使う。

Chrome Web StoreのAPIを用いて拡張機能のzipファイルをアップロード&公開してくれる。

最初に、Google APIのclientId, clientSecret, refreshTokenが必要になる。取得方法は以下。
How to generate Google API keys.md

コマンドは以下。

EXTENSION_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; \
CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; \
CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; \
REFRESH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; \
npx chrome-webstore-upload-cli upload --source extension.zip --extension-id $EXTENSION_ID --client-id $CLIENT_ID --client-secret $CLIENT_SECRET --refresh-token $REFRESH_TOKEN --auto-publish

GitHub Actionsでできそう。
↓参考になりそうな情報
https://github.com/refined-github/refined-github/blob/68bc768230935283e2fdc353a8bcdc2d4b5863fa/.github/workflows/release.yml

eetann / えーたんeetann / えーたん

LinuxでMac風のキーバインドにする

k0kubun/xremapを使った。

環境は以下。

npx envinfo

以下が出力(長いのでちょっと削った)。

  System:
    OS: Linux 5.15 Manjaro Linux
    CPU: (8) x64 Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
    Shell: 5.9 - /usr/bin/zsh
  Binaries:
    Node: 16.14.0 - ~/.volta/tools/image/node/16.14.0/bin/node
    npm: 8.3.1 - ~/.volta/tools/image/node/16.14.0/bin/npm
  Managers:
    Cargo: 1.62.1 - /usr/bin/cargo
  Utilities:
    Make: 4.3 - /usr/bin/make
    GCC: 12.1.0 - /usr/bin/gcc
    Git: 2.37.1 - /usr/bin/git
    Clang: 14.0.6 - /usr/bin/clang
  Languages:
    Bash: 5.1.16 - /usr/bin/bash
    Rust: 1.62.1 - /usr/bin/rustc
物理キー 入れ替え後 mod 役割 i3 Wezterm
ALT_L SUPER_R mod4 Win mod4 Alt
Ctrl_L ALT_L mod1 Alt mod1 Alt
CapsLock SUPER_L mod3 MacのCtrl なし Ctrl
SUPER_L CTRL_L control CTRL なし Ctrl

まず、公式のREADMEどおりにインストール。
AURのxremap-x11-binだとなぜかエラーが出るので、cargo install xremap --features x11のほうにした。

nvim /etc/modules-load.d/uinput.conf
uinput
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' | sudo tee /etc/udev/rules.d/99-input.rules

設定ファイルを書く。

modmap:
  - name: Except Wezterm
    application:
      not: org.wezfurlong.wezterm
    remap:
      ALT_L: SUPER_R
      CTRL_L: ALT_L
      CapsLock: SUPER_L
      SUPER_L: CTRL_L
  - name: Only Wezterm
    application:
      only: org.wezfurlong.wezterm
    remap:
      ALT_L: SUPER_R
      CTRL_L: ALT_L
      CapsLock: CTRL_L
      SUPER_L: CTRL_L
keymap:
  - name: Except Wezterm
    application:
      not: org.wezfurlong.wezterm
    remap:
      SUPER_L-a: home
      SUPER_L-e: end
      SUPER_L-h: backspace
      SUPER_L-d: delete
      SUPER_L-f: right
      SUPER_L-b: left
      SUPER_L-p: up
      SUPER_L-n: down
      SUPER_L-k: [Shift-end, backspace]
      SUPER_L-Tab: CTRL-Tab
      SUPER_L-Shift-Tab: CTRL-Shift-Tab

さらに、以下の記事を参考にして自動起動。
xremapをsystemctlで自動起動するメモ | hyoshi(hara) log

nvim ~/.config/systemd/user/xremap.service
~/.config/systemd/user/xremap.service
[Unit]
Description=xremap

[Service]
KillMode=process
ExecStart=/home/eetann/.cargo/bin/xremap /home/eetann/dotfiles/etc/xremap.yaml
ExecStop=/usr/bin/killall xremap
Restart=always
Environment=DISPLAY=:0.0

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user start xremap
systemctl --user enable xremap
# 以下は不要かもしれない
sudo chown -R $(whoami) /home/eetann/.cargo/

modの割当も変える。

nvim ~/.Xmodmap
clear mod3
clear mod4
add mod3 = Super_L
add mod4 = Super_R
eetann / えーたんeetann / えーたん

問題

chrome.storage.local.setを使っているときに、以下のエラーが出た。

Error handling response: TypeError: Cannot read properties of undefined (reading 'local')

原因

permissionsに"storage"を指定しなかったため。

対策

manifestのpermissionsに"storage"を加える。

manifest.ts
  permissions: ["bookmarks", "storage"],
eetann / えーたんeetann / えーたん

CRXJS Vite Pluginを使ってビルドしたmanifest.jsonを見てみると、以下のようにtypemoduleが指定されている。

manifest.json
  "background": {
    "service_worker": "src/background.ts",
    "type": "module"
  }

これでES modules仕様であるimport文を使えるようになるっぽい。

https://developer.chrome.com/docs/extensions/mv3/service_workers/#manifest

CRXJSはリロードする都合で、勝手に追加してくれてる。

https://crxjs.dev/vite-plugin/concepts/background

eetann / えーたんeetann / えーたん

Arch Linuxでman -k hogeの検索ができなくなったときの対策

現象

man -k hogeを実行すると、以下のようになった。

nothing appropriate.

対策

man page - ArchWikiに書いてあった。

If you are getting a "nothing appropriate" message for every search, try manually regenerating the cache by running mandb as root.

sudo mandb
eetann / えーたんeetann / えーたん

daisyuiと使ってるときに、formatかけると出力がおかしくなる

問題

prettier-plugin-tailwindcss と daisyuiを一緒に使っていると、以下のように変な出力になる。

画像

急に花が咲いたのでビビったw

対策

tailwind.config.jsでdaisyuiのログをオフにする。

tailwind.config.js
module.exports = {
  //...
  daisyui: {
    logs: false,
    //...
  },
}

👇参考
https://github.com/tailwindlabs/tailwindcss/discussions/8380#discussioncomment-2797350

eetann / えーたんeetann / えーたん

疑問

@extend-chrome/storageのgetした時に、未登録のキーを指定したらどうなるのか?

調査

適当な箇所に以下を挿入して調査した。

    getBucket("times")
      .get()
      .then((v) => console.log("times", v, Object.keys(v).length === 0));
    getBucket("times")
      .get("hoge")
      .then((v) => console.log("times:hoge", v, Object.keys(v).length === 0));
    getBucket("foo")
      .get("bar")
      .then((v) => console.log("foo:bar", v, Object.keys(v).length === 0));

以下、出力の例

times:hoge {} true
foo:bar {} true
times {なんか適当な値, …} false

結論

存在しないキーを指定すると、空のオブジェクトが返ってくることが分かった。

eetann / えーたんeetann / えーたん

問題

Redux ToolkitのuseSelectorで引っ張ってきた値をuseEffectの依存配列に入れても、1回しか呼ばれない

↓WIP違うので要調査

# 原因
更新しているのはappearanceなのにそのキーのbucketを指定していた。

  const appearance = useSelector((state: RootState) => state.appearance.bucket);

# 対策
useSelectorの指定を変える。

  const appearance = useSelector((state: RootState) => state.appearance);
eetann / えーたんeetann / えーたん

取得した値を見てみる。

以下で取得

const appearance = useSelector((state: RootState) => state.appearance);

以下で表示

{JSON.stringify(appearance)}

以下が結果。

{"appearance":{"location":"top-right"},"status":"idle"}

eetann / えーたんeetann / えーたん

自分が想定してた出力は以下。

{"bucket":{"location":"top-right"},"status":"idle"}

npm run dev=viteを再実行したらできたw

eetann / えーたんeetann / えーたん

サンプルのChrome拡張機能作るときの手順

随時更新する

npm create vite@latest
cd hoge
npm install
git init
touch README.md

npm create vite@latestの時点で.gitignoreは作成される。

ここらへんで一旦pushすると良さそう。

npm i @crxjs/vite-plugin@2.0.0-beta.4 -D

2022-11-06現在、crxjs/vite-pluginはlatestだとviteのバージョンと干渉するので、betaを使っている。

vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { crx } from '@crxjs/vite-plugin'
import manifest from './src/manifest'

export default defineConfig({
  plugins: [
    react(),
    crx({ manifest }),
  ],
})
manifest.s
import { defineManifest } from "@crxjs/vite-plugin";

export default defineManifest({
  manifest_version: 3,
  name: "Extend Storage Sample",
  version: "1.0.0",
  permissions: ["storage"],
  action: {
    default_popup: "index.html",
  },
});

tsconfig.node.jsonにmanifestを追加

tsconfig.node.json
  "include": ["vite.config.ts", "./src/manifest.ts"]
npm i -D @types/chrome
npx eslint --init
npm i -D eslint-config-prettier \
  eslint-plugin-{react-hooks,unused-imports}
env:
  browser: true
  es2021: true
extends:
  - eslint:recommended
  - plugin:react/recommended
  - plugin:@typescript-eslint/recommended
  - plugin:react-hooks/recommended
  - prettier
parser: "@typescript-eslint/parser"
parserOptions:
  ecmaFeatures:
    jsx: true
  ecmaVersion: latest
  sourceType: module
plugins:
  - react
  - "@typescript-eslint"
  - unused-imports
rules:
  react/jsx-uses-react: off
  react/jsx-uses-vars: off
  react/react-in-jsx-scope: off
npm install --save-dev --save-exact prettier
echo {}> .prettierrc.json
eetann / えーたんeetann / えーたん

DynamoDBのBlack Belt読む

知らなかった単語などをまとめておく
https://pages.awscloud.com/rs/112-TZM-766/images/20170809_AWS-BlackBelt-DynamoDB_rev.pdf

2017年8月の公開であり、古いことに注意。

2022-11-29

  • SPOF(単一障害点 / シングルポイント障害)とは - 意味をわかりやすく - IT用語辞典 e-Words
    • DynamoDBは単一障害点を持たない
  • キャパシティユニット
    • 1秒あたりに何回読み書きできるか、という単位
    • 書き込みの1ユニット = 1KBまでを1秒に1回書き込みできる
    • 読み込み(結果整合性)の1ユニット = 4KBまでを1秒に2回読み込みできる
    • 読み込み(強い整合性)の1ユニット = 4KBまでを1秒に1回読み込みできる
  • プロビジョンドスループット
    • Read・Writeはそれぞれ25キャパシティユニットまで無料
      *
eetann / えーたんeetann / えーたん

DynamoDBのグラレコ解説を読む

https://aws.amazon.com/jp/builders-flash/202206/awsgeek-dynamodb/?awsf.filter-name=*all

2022年6月1日の時点での情報。

eetann / えーたんeetann / えーたん

現象

Chrome拡張機能でPlaywrightを使ってE2Eテストを書いたけど、なぜかうまく動かない

原因

npm run dev(=npx vite)の実行をやめてからnpx playwright testを実行していたことが原因で、コードの変更が反映されていなかったため。

解決法

npm run devしつつテストする。tmuxのウィンドウをケチらない。

eetann / えーたんeetann / えーたん

問題

PlaywrightでChakra UIの要素を指定しようとしたらうまくいなかい

原因

Chakra UIのコンポーネント名は_react=Hogeのように指定できないっぽい?
また、モーダルだと親子関係が変わるのでABC >> DEFのような指定だとうまくいかない。

対策

DevtoolsでElementsをよく確認すること。

eetann / えーたんeetann / えーたん

createAsyncThunkの中で他の関数をdispatchしたい

Redux ToolkitのcreateAsyncThunkの中で他の関数をdispatchしたい時は、以下のように第2引数(thunkAPI)の中のdispatchを取り出して使えば良い。

const addHoge = createAsyncThunk("hoge/foo",
  async (arg: string[], { dispatch }) => {
  dispatch(addManyHoge(arg))
  // ...
}

https://redux-toolkit.js.org/api/createAsyncThunk#payloadcreator:~:text=dispatch%3A the Redux store dispatch method

eetann / えーたんeetann / えーたん

問題

Playwrightで、E2Eを書いていたファイルを分割したら動かなくなった。

原因

分割後のファイル名がhoge.spec.tsではなかったため(specが無かった)。