Open14

Doon - Markdown Notes

まさきちまさきち

状態管理 (Jotai) とデータベース (TypeORM) の使い分け

UI状態: ユーザーインターフェースの現在の状態(例: モーダルの開閉、タブの選択状態)、ログイン状態や一時的なエラーメッセージなど、セッションを通じて持続するが、長期間の永続化を必要としない情報を格納する。

永続的なデータ管理: アプリケーションが再起動しても保持されるべきデータを管理する。

まさきちまさきち

tsconfig.jsonを理解する

https://qiita.com/ryokkkke/items/390647a7c26933940470


tsconfig.jsonのオプションについて

https://typescriptbook.jp/reference/tsconfig


オプションについての説明

https://zenn.dev/chida/articles/bdbcd59c90e2e1


TSConfig Basesの@tsconfig/node16が便利

https://www.npmjs.com/package/@tsconfig/node16

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Node 16",
  "_version": "16.1.0",

  "compilerOptions": {
    "lib": ["es2021"],
    "module": "node16",
    "target": "es2021",

    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node16"
  }
}


Electronの場合、メインプロセス(main)とレンダラープロセス(renderer)で環境が異なるためそれぞれのプロセスのために異なるtsconfig.jsonファイルを設定する。

環境の違い

  • メインプロセスはNode.js環境で動作し、ElectronのAPIやNode.jsのAPIにフルアクセス可能
  • レンダラープロセスはChromium(Web)環境で動作し、WebAPIへのアクセスがメイン

mainプロセス

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "outDir": "./dist/main",
    "lib": ["esnext"],
    "noImplicitAny": true,
    "sourceMap": true
  },
  "include": ["src/main/**/*"],
  "exclude": ["src/renderer/**/*"]
}


renderプロセス

{
  "compilerOptions": {
    "module": "esnext",
    "target": "es6",
    "outDir": "./dist/renderer",
    "lib": ["dom", "esnext"],
    "noImplicitAny": true,
    "sourceMap": true,
    "jsx": "react"
  },
  "include": ["src/renderer/**/*"],
  "exclude": ["src/main/**/*"]
}


package.jsonで調整

"scripts": {
  "build:main": "tsc -p tsconfig.main.json",
  "build:renderer": "tsc -p tsconfig.renderer.json",
  "build": "npm run build:main && npm run build:renderer"
}

tsconfig.node.jsontsconfig.web.jsonが存在する理由→ Electronはメインプロセス(Node.js環境)とレンダラープロセス(Webブラウザ環境)の両方のコードが含まれる為


aliasはelectron.vite.config.tsで設定すればよかった模様

export default defineConfig({
  main: {
    plugins: [externalizeDepsPlugin()],
    resolve: {
      alias: {
        '@/main': resolve('src/main'),
        '@/lib': resolve('src/main/lib')
      }
    }
  },
  preload: {
    plugins: [externalizeDepsPlugin()]
  },
  renderer: {
    assetsInclude: 'src/renderer/assets/**',
    resolve: {
      alias: {
        '@/renderer': path.resolve(__dirname, 'src/renderer'),
        '@/components': resolve('src/renderer/src/components'),
        '@/hooks': resolve('src/renderer/src/hooks'),
        '@/assets': resolve('src/renderer/src/assets'),
        '@/store': resolve('src/renderer/src/store'),
        '@/mocks': resolve('src/renderer/src/mocks')
      }
    },
    plugins: [react()]
  }
})
まさきちまさきち

コンテキストの分離について

コンテキスト分離は、Electronのセキュリティ機能の一つ。レンダラープロセスのグローバルスコープをメインプロセスや他のレンダラープロセスから分離することで、セキュリティ(クロスサイトスクリプティング(XSS)攻撃などから守る)を向上させる。
→ ウェブページのJavaScriptがElectronのAPIやNode.jsの機能に直接アクセスすることを防ぐ。
代わりに、contextBridge APIを使用して、メインプロセスから安全に公開された機能のみをレンダラープロセスで利用できる。

contextBridge.exposeInMainWorld(apiKey, api)


コンテキストが分離されているかどうかを確認する

if(process.contextIsolated) {
...
}
まさきちまさきち

Electron vita

https://electron-vite.org/guide/introduction



Scssで以下エラーが発生

[plugin:vite:css] [sass] Can't find stylesheet to import.
  ╷
2 │ @use '@/styles/libs/mixins/index' as *;
  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ╵
  src/renderer/src/styles/_base.scss 2:1   @use
  src/renderer/src/styles/styles.scss 1:1  root stylesheet

ViteがScssのpathを解決できていないことが原因である模様

まさきちまさきち

nagative merginの使い所
例えば、ヘッダー、フッターの左右と上下の位置は、ページにぴったり配置したいが、親の余白があるために子の配置がうまくいかない場合など。
https://zenn.dev/smartshopping/articles/658ac0e7c064b1


mixinのtips

https://zenn.dev/tak_dcxi/articles/2cc1828e9c1fe2


cssのテクニック

https://www.tak-dcxi.com/article/that-css-technique-you-learned-is-outdated/


CSS入れ子チートシート

https://yoshikawaweb.com/element/

まさきちまさきち

React.ComponentPropsについて

https://zenn.dev/takepepe/articles/atoms-type-definitions

https://zenn.dev/tm35/articles/4bc94d7e6cb314

Props 型定義を自分で用意した場合、メンテナンスコストが高い為お勧めしない。実装が増えるたびに都度都度値を追加していく必要があるため。

type Props = {
  value: string;
  onChange?: React.ChangeEventHandler<HTMLInputElement>
  onBlur?: React.FocusEventHandler<HTMLInputElement>
}
export const Input = ({ value, onChange, onBlur }: Props) => (
  <input
    value={value}
    onChange={onChange}
    onBlur={onBlur}
    className={styles.input}
   />
)

React.ComponentProps 型を使う

type Props = React.ComponentProps<'input'>

export const Input = ({ className, ...props }: Props) => (
  <input
    {...props} // <- className 以外、全ての props を分割代入
    className={clsx(className, styles.input)}
  />
)


React.ComponentPropsよりもComponentPropsWithoutRefを使用することでrefを許容しているかを確認できる。

https://kk-web.link/blog/20201023

まさきちまさきち

Component設計



Atomic Designについてあらためて評価する

https://zenn.dev/mutex_inc/articles/beca85dd7fdcae

Atomic Designの場合、コンポーネントの粒度を決定する基準が曖昧になることがある。例えば、所望のコンポーネントをOrganismsに作ったらいいのか、Moleculesに作ったらいいのか迷う場面がある。

Hidden comment
まさきちまさきち

状態管理ライブラリ

https://zenn.dev/kazukix/articles/react-state-management-libraries

https://blog.uhy.ooo/entry/2021-07-24/react-state-management/

▪️ 候補

  • Redux

コードが膨大になりやすい。より簡単に記載できるRedux Toolkitは存在する。
DispatchとReducerが存在し、メンテナンスコストが大きい。
すべてのステートを一つのStoreで管理し、selectorにより必要なデータのみ抽出する。
ルールが厳密で大規模開発向き

  • Recoil

AtomとSelector:Atomは一つの状態を保持する(Reduxとは異なりデータのソースとなるAtomが複数存在する)
ReduxのDispatchとReducerによる中間操作が不要
対象のコンポーネントを<RecoilRoot/>で囲む。Atom を作成する際はkeyを指定する必要あり。
keyの管理をどのようにするか。

  • Zustand

Redux に近い

  • Jotai

コンポーネントで Atomの使用が可能で、プロバイダーでラップする必要なし。keyも不要
シンプルで直感的に操作できるJotaiを採用する。

https://jotai.org/



Reactの歴史

https://ics.media/entry/200310/

まさきちまさきち

MDX Editor

マークダウンの生成にMDX Editorを使用する。
https://mdxeditor.dev/editor/docs/getting-started


ToolbarCode blocksが直感的に記述することができ良さげ

MDXEditorを使用したところ、以下のエラーが発生

Uncaught EvalError: Refused to evaluate a string as JavaScript
because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".

    at eval (<anonymous>)
    at getGlobal (@mdxeditor_editor.js?v=1930ed33:65343:10)
    at @mdxeditor_editor.js?v=1930ed33:65344:2

Content Security Policy(CSP)に関連したエラー:XSS攻撃などから保護するために、どのような外部リソースがWebページに読み込まれることを許可するかをブラウザに指示するためのもの
Electornではunsafe-eval を許可しないようにデフォルト設定されている。

MDXEditorや依存ライブラリがeval()関数を使用しているために、警告文がconsole.logに表示されている模様。

eval() 関数は文字列から JavaScript を実行する関数。

一応手段としては、'unsafe-eval' を CSP ルールに追加することは可能(セキュリティリスクは増加する)。

<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval';">


代替案

react-markdown, remarkなど別のMDXを検討する。

https://blog.stin.ink/articles/replace-react-markdown-with-remark

  • react-markdown
  • SimpleMDE (react-simplemde-editor)
  • Draft.js
  • ProseMirror

react-markdownを採用する

https://github.com/remarkjs/react-markdown?tab=readme-ov-file