Migration

ここでは minista v3 から v4 への移行方法について説明します。

Table of Contents

Overview

v4 は設計が大きく変わっています。v3 まではオールインワンのライブラリでしたが、v4 からは本体が Vite のラッパーとして動作し、SSG や画像最適化などの機能はすべて Vite プラグインとして提供されます。

Changes

  • すべての機能を Vite プラグイン化
  • コンフィグを Vite 公式に変更
  • インポートパスを細分化して変更
  • Svgr を Svg コンポーネントに変更
  • Icon を Sprite に変更
  • Partial Hydration を Island に変更
  • Delivery(納品リスト)を廃止

Agent Mode

まず、エディタのエージェントモードを使用して効率的に移行する方法を検討してください。GitHub 上にこのページの Markdown データがありますので、エージェントへの指示としてコピーして使用(またはダウンロードして指示に添付)し、その上でうまくいかなかった箇所を修正してみてください。

Packages

最初にパッケージを更新します。v4 では vite も peerDependencies になりました。

npm install --save-dev minista@latest vite react react-dom

TypeScript を使用している場合は @types/react @types/react-dom も更新。

npm install --save-dev @types/react @types/react-dom

Partial Hydration や Search の移行には @vitejs/plugin-react が必要です。

npm install --save-dev @vitejs/plugin-react

Config

次にコンフィグファイルを更新します。v4 は minista 独自のコンフィグを廃止し Vite 公式と同一になりました。ファイルは minista.config.{ts,js} または vite.config.{ts,js} のどちらでも動作します。

基本設定は Vite 公式に沿って移行。minista 独自の機能は Vite プラグイン化されているため、必要なプラグインをインポートして plugins 配列に追加します。

注意点として、v4 は Vite の SSR ビルドと通常ビルドを連続で行うことを意識してください。ビルド用に設定した内容が SSR ビルドを壊してしまう可能性があります。これは設定を SSR ビルド用・通常ビルド用に切り分けることで解消できます。

Before
import { defineConfig } from "minista"

export default defineConfig({
  out: "dist",
  assets: {
    images: {},
  },
})
After
import { defineConfig, pluginSsg, pluginImage } from "minista"

export default defineConfig(({ command, isSsrBuild }) => {
  const isDev = command === "serve"
  const isSsr = command === "build" && isSsrBuild
  const isBuild = command === "build" && !isSsrBuild
  return {
    plugins: [pluginSsg()],
    build: { outDir: isBuild ? "dist" : undefined },
  }
})

root

※新旧ともに Vite と同じなので変更なし

base

※新旧ともに Vite と同じなので変更なし

public

Vite の publicDir に変更してください。

Before
export default { public: "public" }
After
export default { publicDir: "public" }

out

Vite の build.outDir に変更してください。

Before
export default { out: "dist" }
After
export default { build: { outDir: "dist" } }

assets.outDir

Vite の build.assetsDir に変更してください。

Before
export default { assets: { outDir: "assets" } }
After
export default { build: { assetsDir: "assets" } }

assets.outName

Vite の build.rollupOptions でそれぞれ設定する必要があります。

  • CSS・画像・フォント: build.rollupOptions.output.assetFileNames
  • JavaScript(chunk): build.rollupOptions.output.chunkFileNames
  • JavaScript(entry): build.rollupOptions.output.entryFileNames

※これらを設定した場合 build.assetsDir は無視されます。

Before
export default { assets: { outName: "pjt-[name]" } }
After
export default {
  build: {
    rollupOptions: {
      output: {
        assetFileNames: "assets/pjt-[name][extname]",
        chunkFileNames: "assets/pjt-[name].js",
        entryFileNames: "assets/pjt-[name].js",
      },
    },
  },
}

assets.images

outDir, outName

Vite の build.rollupOptions で設定する必要があります。後述の Separate asset directories を参照して設定してください。

remoteName

minista のプラグイン pluginImage のオプションに変更してください。v3 では固定だった [index] プレースホルダーの位置を指定できます。

Before
export default { assets: { images: { remoteName: "remote" } } }
After
import { pluginImage } from "minista"

export default {
  plugins: [pluginImage({ optimize: { remoteName: "remote-[index]" } })],
}

optimize

minista のプラグイン pluginImage のオプションに変更してください。v3 では固定だった出力画像の名前([name]-[width]x[height])を変更できます。

Before
export default { assets: { images: { optimize: { layout: "constrained" } } } }
After
import { pluginImage } from "minista"

export default {
  plugins: [
    pluginImage({
      optimize: { name: "[name]-[width]x[height]", layout: "constrained" },
    }),
  ],
}

assets.svgr

svgrOptions

SVGR が提供するオプションの中で svgoConfig のみ pluginSvg のオプションとしてそのまま移行できます。その他のオプションはサポートされていないため、後述の Svgr 移行を参照して対応してください。

Before
export default {
  assets: { svgr: { svgrOptions: { svgoConfig: { plugins: [] } } } },
}
After
import { pluginSvg } from "minista"

export default { plugins: [pluginSvg({ plugins: [] })] }

assets.icons

後述により Icon は Sprite に移行となりますが、事前にオプションの移行先を記載します。

srcDir, outName

オプションとして指定することはなくなり、テンプレートに設置した Sprite の src により実質的に複数のディレクトリをターゲットにできるようになりました。また、src のファイルが存在するディレクトリ名が出力名になります。

Before
export default {
  assets: { icons: { srcDir: "src/assets/icons", outName: "[dirname]" } },
}
After (template)
import { Sprite } from "minista/assets"

export default function () {
  return <Sprite src="/src/assets/icons/square.svg" />
}

outDir

Vite の build.rollupOptions で設定する必要があります。後述の Separate asset directories を参照して設定してください。

svgstoreOptions

依存関係の SVG 最適化ライブラリが svgstore から svgo に変更となったため、同様のオプションは存在しません。pluginSpritesvgo オプションを渡すことで最適化を図ってください。

Before
export default {
  assets: {
    icons: {
      svgstoreOptions: {
        cleanSymbols: ["fill", "stroke", "stroke-linejoin", "stroke-width"],
      },
    },
  },
}
After
import { pluginSprite } from "minista"

export default {
  plugins: [
    pluginSprite({
      plugins: [
        {
          name: "removeAttrs",
          params: {
            attrs: ["fill", "stroke", "stroke-linejoin", "stroke-width"],
          },
        },
      ],
    }),
  ],
}

assets.fonts

outDir, outName

Vite の build.rollupOptions で設定する必要があります。後述の Separate asset directories を参照して設定してください。

assets.bundle

outName

minista のプラグイン pluginBundle のオプションに変更してください。

Before
export default { assets: { bundle: { outName: "bundle" } } }
After
import { pluginBundle } from "minista"

export default { plugins: [pluginBundle({ outName: "bundle" })] }

assets.partial

後述により Partial Hydration は Island に移行となりますが、事前にオプションの移行先を記載します。

usePreact

自動切り替えは廃止されたため、Vite の resolve.alias を使用してください。

Before
export default { assets: { partial: { usePreact: true } } }
After
const preactAlias = {
  react: "preact/compat",
  "react-dom": "preact/compat",
}

export default defineConfig(({ command, isSsrBuild }) => {
  const isDev = command === "serve"
  const isSsr = command === "build" && isSsrBuild
  const isBuild = command === "build" && !isSsrBuild
  return {
    resolve: {
      alias: isBuild ? preactAlias : undefined,
    },
  }
})

preact のインストールも必要となります。

$ npm install preact

useIntersectionObserver

IntersectionObserver を使うかどうかはコンフィグではなくコンポーネントのディレクティブで指定するようになりました。client:visible を使用してください。

Before
export default { assets: { partial: { useIntersectionObserver: true } } }
After (template)
import { Counter } from "../components/counter"

export default function () {
  return <Counter client:visible />
}

outName

minista のプラグイン pluginIsland のオプションに変更してください。v3 では固定だった [index] プレースホルダーの位置を指定できます。

Before
export default { assets: { partial: { outName: "hydrate" } } }
After
import { pluginIsland } from "minista"

export default { plugins: [pluginIsland({ outName: "hydrate-[index]" })] }

rootAttrSuffix, rootValuePrefix

rootAttrSuffix は minista のプラグイン pluginIsland のオプションに変更してください。rootValuePrefix で設定していた識別子の接頭辞は廃止され、HTML には数字のみ記載されます。

Before
export default {
  assets: {
    partial: { rootAttrSuffix: "partial-hydration", rootValuePrefix: "ph" },
  },
}
After
import { pluginIsland } from "minista"

export default {
  plugins: [pluginIsland({ rootAttrName: "partial-hydration" })],
}

ただし、ハイドレーション対象のルート要素に付与される属性名は機能の細分化に伴い変更されているので、同一の値を渡しても異なる名前になります。CSS や JavaScript で参照している場合は注意してください。

Before
<div data-partial-hydration="ph-1">...</div>
After
<div
  data-partial-hydration-client-directive="load"
  data-partial-hydration-client-directive-params
  data-partial-hydration-client-snippet="1"
>
  ...
</div>

rootDOMElement, rootStyle

minista のプラグイン pluginIsland のオプションに変更してください。

Before
export default {
  assets: {
    partial: { rootDOMElement: "div", rootStyle: { display: "contents" } },
  },
}
After
import { pluginIsland } from "minista"

export default {
  plugins: [
    pluginIsland({ rootDOMElement: "div", rootStyle: { display: "contents" } }),
  ],
}

intersectionObserverOptions

IntersectionObserver の設定はそれが使用されるコンポーネントの client:visible ディレクティブで指定するようになりました。

Before
export default {
  assets: { partial: { intersectionObserverOptions: { rootMargin: "0px" } } },
}
After (template)
import { Counter } from "../components/counter"

export default function () {
  return <Counter client:visible={{ rootMargin: "0px" }} />
}

resolve.alias

※新旧ともに Vite と同じなので変更なし

css

※新旧ともに Vite と同じなので変更なし

markdown

Vite プラグイン pluginsMdx のオプションに変更となりますが、一部のプロパティは廃止されており個別設定となります。

Separate asset directories

Vite のデフォルト設定では、すべてのアセットが同じディレクトリに出力されます。CSS・JavaScript・画像・フォントを別々のディレクトリに出力したい場合は、以下のプロパティを設定してください。

  • CSS・画像・フォント: build.rollupOptions.output.assetFileNames
  • JavaScript(chunk): build.rollupOptions.output.chunkFileNames
  • JavaScript(entry): build.rollupOptions.output.entryFileNames

※これらを設定した場合 build.assetsDir は無視されます。

assetFileNames は拡張子ごとに分けることができます。

./minista.config.ts
import type { PreRenderedAsset } from "rolldown"
import { normalizePath } from "vite"

const assetFileNames = (assetInfo: PreRenderedAsset) => {
  const name = assetInfo.name ?? ""
  const originalNames = assetInfo.originalFileNames ?? []
  const isSprite = originalNames.some((file) =>
    normalizePath(file).includes("/.minista/sprite/"),
  )
  if (name.endsWith(".css")) {
    return "assets/css/[name][extname]"
  }
  if (isSprite) {
    return "assets/sprites/[name][extname]"
  }
  if (/\.(png|jpe?g|gif|bmp|svg|webp|avif)$/.test(name)) {
    return "assets/images/[name][extname]"
  }
  if (/\.(woff2?|ttf|otf|eot)$/.test(name)) {
    return "assets/fonts/[name][extname]"
  }
  return "assets/others/[name][extname]"
}

export default {
  build: {
    rollupOptions: {
      output: {
        assetFileNames,
        chunkFileNames: "assets/js/[name].js",
        entryFileNames: "assets/js/[name].js",
      },
    },
  },
}