かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Next.js SVG ファイルを React component として import したい!

create-react-app した React アプリでは何もしなくても SVG ファイルを import { ReactComponent as MySVGImage } from './svg/my-image.svg'; で読み込みコンポーネントとして扱うことができました。

Next.js でも同じことができると思ったのですが、create-next-app しただけの状態では SVG ファイルをそのまま import することができませんでした。(エラーになる)

create-next-app した Next アプリで SVG ファイルを React component として import できるようにする!

1. babel-plugin-inline-react-svg を導入する

$ npm -i D babel-plugin-inline-react-svg 

1-2. .babelrc にプラグインの設定をする

root に .babelrc ファイルを作成して inlne-react-svg を使えるようにする

.babelrc

{
  "presets": ["next/babel"],
  "plugins": ["inline-react-svg"]
}

これで import MySVGImage from './svg/my-image.svg'; のように SVG ファイルを React Component として読み込めるようになりました! (Named import ではないですが)

2. SVG の import にも path.alias が使いたい!

コンポーネントから SVG を読み込ませる時に ../../../../svg/my.svg のようにインポートするのはちょい辛いです。

Next で path.alias を使う

tsconfig.json に下記設定を加えると @/./src/ フォルダに、 ~/public/public フォルダに直接アクセス可能となります。

{
  "baseUrl": "src",
    "paths": {
      "@/*": ["./*"],
      "~/public/*": ["../*"]
    },
}

上記のNext.js 側で path.alias を指定していたので SVG でもそのまま @/svg/my.svg のように import できるものだと思っていたのですが、パスが解決できずエラーになってしまいました。

2-1. SVG ファイルにパスエイリアスを適応するには別途 babel-plugin-module-resolver で設定が必要

SVG は babel を通じて import できるようになっているので、パスの解決も babel での設定が必要なようです

$ npm i -D babel-plugin-module-resolver

2-2. .babelrc にエイリアスの設定を追加する

.babelrc

{
  "presets": ["next/babel"],
  "plugins": [
    "inline-react-svg",
+   [
+     "module-resolver",
+     {
+       "alias": {
+         "@": "./src/"
+       }
+     }
+   ]
  ]
}

"module-resolver" の設定で baseUrl も指定可能だったのですが、baseUrl: "./src" とすると tsconfig での設定とバッティングしてしまうようで SVG 以外の エイリアスを使ったインポートの箇所でエラーが出てしまいました。
上記の例のように baseUrl 指定なしで、alias に直接 .babelrc のある階層からのパスを指定してあげればバッティングすることなく同じパスエイリアスが使えるようになりました!

所感

Next.js でも create-react-app と同じ様に使えるんだと思っていたら別途設定が必要だったのですが、公式に例が合ったおかげでサクッと実現することができました。(named import にする方法は調べなかった) パスエイリアスの方は少しハマってしまいましたが、一度設定の作り方解ってしまえば次に活かせるのでヨシ!(๑•̀ㅂ•́)و✧
環境作り王に俺はなる…!!


[参考]

かげきしょうじょ!! 見てください

Svelte 子コンポーネントのスタイルを親コンポーネントで指定したい

LP作って〜って言われたので、ほな Svelte でやってみよっかな〜と思い Svelte に入門しました。

Svelte はデフォルトでは CSS in JS でスコープのあるスタイルが書けるのですが、共通して使いまわしているコンポーネントのスタイルを親コンポーネントで指定するのにちょいハマったのでメモ。

Svelte プロジェクトの作成

$ npx degit sveltejs/template my-project
$ cd my-project
$ npm install
# TypeScript 化 (設定勝手にやってくれる)
$ node scripts/setupTypeScript.js
# Sass 使えるようにする
$ npm i -D sass

Sass の利用は TypeScript 化をしていたからなのかは不明ですが sass をインストールするだけで特に設定をしなくても .svelte ファイル内で <style lang="scss"> とすれば使えるようになりました。

  • svelte: ^3.0.0
  • rollup: ^2.3.4
  • typescript: ^4.0.0
  • sass: 1.37.5

コンポーネントのスタイルを親コンポーネントで指定する

Logo コンポーネント

// src/Logo.svelte
<img class="logo" src="/images/logo.svg" alt="ロゴ" />

App コンポーネント

// src/App.svelte
<script lang="ts">
  import Logo from './Logo.svelte';
</script>

<main role="main">
  <Logo />
</main>

<style lang="scss">
  .logo { widh: 100px; }
</style>

App コンポーネントから Logo の width を指定したいけど、Svelte の <style> は書かれている DOM だけをスコープにするようで、上記では上手く動作しません

:global を使う方法

:global(selector) でスコープのないグローバルなクラスが作成できることを利用して、このコンポーネントのクラス :global(子コンポーネントのクラス) で該当コンポーネントの時だけ該当するスタイルになる

// src/App.svelte
// Logo => <img class="logo" src="/images/logo.svg" alt="ロゴ" />

<main class="main" role="main">
  <Logo />
</main>

<style lang="scss">
  .main :global(.logo) { widh: 100px; }
</style>

この方法は global な CSS としてスタイルが作成されるので、親コンポーネントから子コンポーネントのスタイルを作成することができます。
ただ、親コンポーネントが子コンポーネントのクラス名やタグ名などの内部実装を知っていなければならないのでチョット微妙です。

cf. CSS Modules やっていき - かもメモ :global とか :local についてのメモ

コンポーネントからスコープ付きのクラス名を渡す方法

必要なパッケージのインストール

$ npm i -D svelte-preprocess-cssmodules svelte-as-markup-preprocessor

ビルドの設定の変更

rollup.config.js

+ const cssModules = require('svelte-preprocess-cssmodules');
+ const { asMarkupPreprocessor } = require('svelte-as-markup-preprocessor');
// …

export default {
  // …
  plugins: [
    svelte({
-     preprocess: sveltePreprocess({ sourceMap: !production }),
+      preprocess: [
+       asMarkupPreprocessor([
+         sveltePreprocess({ sourceMap: !production }),
+       ]),
+       cssModules(),
+     ],
      compilerOptions: {
        // enable run-time checks when not in production
        dev: !production
      },
    }),
  ],

コンポーネントからクラス名を指定できるようにする

/src/Logo.svelte

<script lang="ts">
  const addClass = $$restProps.class || '';
  const className = [...new Set(['logo', addClass])].join(' ');
</script>

<img class={className} src="/images/logo.svg" alt="ロゴ" />

コンポーネントで子コンポーネントのスタイルを指定する

src/App.svelte

<script lang="ts">
  import Logo from './Logo.svelte';
</script>

<main role="main">
  <Logo class="main-logo" />
</main>

// module 属性を付ける
<style lang="scss" module>
  .main-logo { widh: 100px; }
</style>

ポイントは親コンポーネント<style>module 属性を付けることです! module 属性が無いと子コンポーネントに上手くスタイルが設定できませんでした。

cf. Support classes on nested components by nikku · Pull Request #2888 · sveltejs/svelte · GitHub

<style module> の時に気をつけること

<style module> の時 p {} のようなタグでスタイルを指定していると意図せず global なスタイルを作成してしまう事になるので注意が必要です

module でない時
<section>
  My Content
</section>

<style lang="scss">
  section { width: 100%; }
</style>

👇 コンパイルするとタグ指定のものは自動でクラス名が付けられる

<section class="svelte-1dsdi5i">
  My Content
</section>

<style>
section.svelte-1dsdi5i { width: 100%; }
</style>
module の時
<section>
  My Content
</section>

<style lang="scss" module>
  section { width: 100%; }
</style>

👇 コンパイルしてもタグ指定のままになる

<section>
  My Content
</section>

<style>
section { width: 100%; }
</style>

全ての <section> に適応されるクラスになってしまう!!!

Svelte でスタイルを <style module> にする時は必ずクラスを付けてスタイルを適応したほうが安全!

これで npm run dev 又は npm run build で確認すると .main-logo のクラス名がハッシュ化され Logo コンポーネントに渡され App コンポーネントで指定したスタイルが適応されるようになりました!
₍ ᐢ. ̫ .ᐢ ₎ A W E S O M E !

おわり

新しい事を学ぶのは楽しい!
納期が無ければもっと楽しい…!!
だが、納期がないと手を付けない…!!!!


[参考]

nodenv でインストールしたいバージョンが表示されない時にやること

nodenv install --list でインストールしたいバージョンが見当たらない

nodenv で新しい node のバージョンをインストーしようとしてインストール可能なリストを表示…

$ nodenv install --list

インストールしたいバージョンが表示されない…

nodenv がアップデートされてないのが原因

nodenv は anyenv でインストールしました。
その nodenv のある場所はデフォルトだと ./anyenv/envs/nodenv でコードは git 管理されており、その中にある plugins/node-build が最新になってないと nodenv install --list で表示されるリストは変わらないようです。
このディレクトリに入って git pull しても良いのですが、チョットメンドクサイです…

anyenv-update で anyenv でインストールした *env を一括アップデートする

nodenv 以外にも pyenv, rbenv と anyenv で入れたものを丸っとアップデートできるので良さそうです。
導入方法は GitHub の README にある通り、anyenv のディレクトリ内に plugins ディレクトリを作成してそこにコードを git clone するだけ!

$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update

これで anyenv update コマンドが使えるようになり、これを実行すると *env が丸っとアップデートされます

$ anyenv update

アップデートが終わったらインストール可能なバージョンのリストが更新されていることを確認できれば OK

$ nodenv install --list

₍ ᐢ. ̫ .ᐢ ₎ カンタン!!


[参考]

夏バテには塩分!