Migration
ここではminista v3からv4への移行方法について説明します。
Table of Contents
- Overview
- Changes
- Agent Mode
- Packages
- Config
- Import Paths
- Svgr to Svg
- Icon to Sprite
- Partial Hydration to Island
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-domTypeScriptを使用している場合は @types/react @types/react-dom も更新。
npm install --save-dev @types/react @types/react-domPartial HydrationやSearchの移行には @vitejs/plugin-react が必要です。
npm install --save-dev @vitejs/plugin-reactConfig
次にコンフィグファイルを更新します。v4はminista独自のコンフィグを廃止しVite公式と同一になりました。ファイルは vite.config.{ts,js} または minista.config.{ts,js} のどちらでも動作します。
基本設定はVite公式に沿って移行。minista独自の機能はViteプラグイン化されているため、必要なプラグインをインポートして plugins 配列に追加します。
注意点として、v4はViteのSSRビルドと通常ビルドを連続で行うことを意識してください。ビルド用に設定した内容がSSRビルドを壊してしまう可能性があります。これは設定をSSRビルド用・通常ビルド用に切り分けることで解消できます。
import { defineConfig } from "minista"
export default defineConfig({
out: "dist",
assets: {
images: {},
},
})import { defineConfig, pluginSsg, pluginImage } from "minista"
export default defineConfig(({ command, isSsrBuild }) => {
const isDev = command === "serve"
const isSsr = command === "build" && (isSsrBuild ?? false)
const isBuild = command === "build" && !(isSsrBuild ?? false)
return {
plugins: [pluginSsg()],
build: { outDir: isBuild ? "dist" : undefined },
}
})root
※新旧ともにViteと同じなので変更なし
base
※新旧ともにViteと同じなので変更なし
public
Viteの publicDir に変更してください。
export default { public: "public" }export default { publicDir: "public" }out
Viteの build.outDir に変更してください。
export default { out: "dist" }export default { build: { outDir: "dist" } }assets.outDir
Viteの build.assetsDir に変更してください。
export default { assets: { outDir: "assets" } }export default { build: { assetsDir: "assets" } }assets.outName
Viteの build.rolldownOptions でそれぞれ設定する必要があります。
- CSS・画像・フォント:
build.rolldownOptions.output.assetFileNames - JavaScript(chunk):
build.rolldownOptions.output.chunkFileNames - JavaScript(entry):
build.rolldownOptions.output.entryFileNames
※これらを設定した場合 build.assetsDir は無視されます。
export default { assets: { outName: "pjt-[name]" } }export default {
build: {
rolldownOptions: {
output: {
assetFileNames: "assets/pjt-[name][extname]",
chunkFileNames: "assets/pjt-[name].js",
entryFileNames: "assets/pjt-[name].js",
},
},
},
}assets.images
outDir, outName
Viteの build.rolldownOptions で設定する必要があります。後述のSeparate asset directoriesを参照して設定してください。
remoteName
ministaのプラグイン pluginImage のオプションに変更してください。v3では固定だった [index] プレースホルダーの位置を指定できます。
export default { assets: { images: { remoteName: "remote" } } }import { pluginImage } from "minista"
export default {
plugins: [pluginImage({ optimize: { remoteName: "remote-[index]" } })],
}optimize
ministaのプラグイン pluginImage のオプションに変更してください。v3では固定だった出力画像の名前([name]-[width]x[height])を変更できます。
export default { assets: { images: { optimize: { layout: "constrained" } } } }import { pluginImage } from "minista"
export default {
plugins: [
pluginImage({
optimize: { outName: "[name]-[width]x[height]", layout: "constrained" },
}),
],
}assets.svgr
svgrOptions
SVGRが提供するオプションの中で svgoConfig のみ pluginSvg のオプションとしてそのまま移行できます。その他のオプションはサポートされていないため、後述のSvgr移行を参照して対応してください。
export default {
assets: { svgr: { svgrOptions: { svgoConfig: { plugins: [] } } } },
}import { pluginSvg } from "minista"
export default { plugins: [pluginSvg({ config: { plugins: [] } })] }assets.icons
後述によりIconはSpriteに移行となりますが、事前にオプションの移行先を記載します。
srcDir, outName
オプションとして指定することはなくなり、テンプレートに設置したSpriteの src により実質的に複数のディレクトリをターゲットにできるようになりました。また、src のファイルが存在するディレクトリ名が出力名になります。
export default {
assets: { icons: { srcDir: "src/assets/icons", outName: "[dirname]" } },
}import { Sprite } from "minista/assets"
export default function () {
return <Sprite src="/src/assets/icons/square.svg" />
}outDir
Viteの build.rolldownOptions で設定する必要があります。後述のSeparate asset directoriesを参照して設定してください。
svgstoreOptions
依存関係のSVG最適化ライブラリが svgstore から svgo に変更となったため、同様のオプションは存在しません。pluginSprite に svgo オプションを渡すことで最適化を図ってください。
export default {
assets: {
icons: {
svgstoreOptions: {
cleanSymbols: ["fill", "stroke", "stroke-linejoin", "stroke-width"],
},
},
},
}import { pluginSprite } from "minista"
export default {
plugins: [
pluginSprite({
config: {
plugins: [
{
name: "removeAttrs",
params: {
attrs: ["fill", "stroke", "stroke-linejoin", "stroke-width"],
},
},
],
},
}),
],
}assets.fonts
outDir, outName
Viteの build.rolldownOptions で設定する必要があります。後述のSeparate asset directoriesを参照して設定してください。
assets.bundle
outName
ministaのプラグイン pluginBundle のオプションに変更してください。
export default { assets: { bundle: { outName: "bundle" } } }import { pluginBundle } from "minista"
export default { plugins: [pluginBundle({ outName: "bundle" })] }assets.partial
後述によりPartial HydrationはIslandに移行となりますが、事前にオプションの移行先を記載します。
usePreact
自動切り替えは廃止されたため、Viteの resolve.alias を使用してください。
export default { assets: { partial: { usePreact: true } } }const preactAlias = {
react: "preact/compat",
"react-dom": "preact/compat",
}
export default defineConfig(({ command, isSsrBuild }) => {
const isDev = command === "serve"
const isSsr = command === "build" && (isSsrBuild ?? false)
const isBuild = command === "build" && !(isSsrBuild ?? false)
return {
resolve: {
alias: isBuild ? preactAlias : undefined,
},
}
})preact のインストールも必要となります。
$ npm install preactuseIntersectionObserver
IntersectionObserverを使うかどうかはコンフィグではなくコンポーネントのディレクティブで指定するようになりました。client:visible を使用してください。
export default { assets: { partial: { useIntersectionObserver: true } } }import { Counter } from "../components/counter"
export default function () {
return <Counter client:visible />
}outName
ministaのプラグイン pluginIsland のオプションに変更してください。v3では固定だった [index] プレースホルダーの位置を指定できます。
export default { assets: { partial: { outName: "hydrate" } } }import { pluginIsland } from "minista"
export default { plugins: [pluginIsland({ outName: "hydrate-[index]" })] }rootAttrSuffix, rootValuePrefix
rootAttrSuffix はministaのプラグイン pluginIsland のオプションに変更してください。rootValuePrefix で設定していた識別子の接頭辞は廃止され、HTMLには数字のみ記載されます。
export default {
assets: {
partial: { rootAttrSuffix: "partial-hydration", rootValuePrefix: "ph" },
},
}import { pluginIsland } from "minista"
export default {
plugins: [pluginIsland({ rootAttrName: "partial-hydration" })],
}ただし、ハイドレーション対象のルート要素に付与される属性名は機能の細分化に伴い変更されているので、同一の値を渡しても異なる名前になります。CSSやJavaScriptで参照している場合は注意してください。
<div data-partial-hydration="ph-1">...</div><div
data-partial-hydration-client-directive="load"
data-partial-hydration-client-directive-params
data-partial-hydration-client-snippet="1"
>
...
</div>rootDOMElement, rootStyle
ministaのプラグイン pluginIsland のオプションに変更してください。
export default {
assets: {
partial: { rootDOMElement: "div", rootStyle: { display: "contents" } },
},
}import { pluginIsland } from "minista"
export default {
plugins: [
pluginIsland({ rootDOMElement: "div", rootStyle: { display: "contents" } }),
],
}intersectionObserverOptions
IntersectionObserverの設定はそれが使用されるコンポーネントの client:visible ディレクティブで指定するようになりました。
export default {
assets: { partial: { intersectionObserverOptions: { rootMargin: "0px" } } },
}import { Counter } from "../components/counter"
export default function () {
return <Counter client:visible={{ rootMargin: "0px" }} />
}resolve.alias
※新旧ともにViteと同じなので変更なし
css
※新旧ともにViteと同じなので変更なし
markdown
Viteプラグイン pluginMdx のオプションに変更となります。v3でministaが用意していた useRemarkGfm useRehypeHighlight は廃止されたため、必要なRemark / Rehypeプラグインをインストールして remarkPlugins rehypePlugins に渡してください。
useRemarkGfm, remarkGfmOptions
remark-gfm を直接設定してください。
export default {
markdown: {
useRemarkGfm: true,
remarkGfmOptions: {},
},
}import { pluginMdx } from "minista"
import remarkGfm from "remark-gfm"
export default {
plugins: [pluginMdx({ remarkPlugins: [[remarkGfm, {}]] })],
}useRehypeHighlight, rehypeHighlightOptions
rehype-highlight を直接設定してください。また、見た目の調整まで含めたい場合は rehype-pretty-code など別のRehypeプラグインへの移行も検討してください。
export default {
markdown: {
useRehypeHighlight: true,
rehypeHighlightOptions: {},
},
}import { pluginMdx } from "minista"
import rehypeHighlight from "rehype-highlight"
export default {
plugins: [pluginMdx({ rehypePlugins: [[rehypeHighlight, {}]] })],
}mdxOptions
mdxOptions の中身は pluginMdx の直下に移動してください。
export default {
markdown: {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [],
},
},
}import { pluginMdx } from "minista"
export default {
plugins: [pluginMdx({ remarkPlugins: [], rehypePlugins: [] })],
}search
ministaのプラグイン pluginSearch のオプションに変更してください。検索UIはIslandとして動作するため、pluginIsland と @vitejs/plugin-react も併用します。
export default {
search: {
outDir: "assets",
outName: "search",
include: ["**/*"],
exclude: ["/404"],
baseUrl: "",
trimTitle: "",
targetSelector: "[data-search]",
},
}import { pluginIsland, pluginSearch } from "minista"
import react from "@vitejs/plugin-react"
export default {
plugins: [
pluginIsland(),
pluginSearch({
outName: "search",
src: ["**/*.html"],
ignore: ["404.html"],
trimTitle: "",
targetSelector: "[data-search]",
}),
react(),
],
}outDir
検索用JSONもViteのアセットとして出力されるため、個別の outDir は廃止されました。出力先を変える場合は build.rolldownOptions.output.assetFileNames で制御してください。
include, exclude
include は src、exclude は ignore に変更してください。対象はSSG後のHTMLファイルなので、"/404" のようなURLパスではなく "404.html" のようなファイル名のglobを指定します。
baseUrl
baseUrl は廃止されました。URLの基準はViteの base から解決されます。
hit
hit の中身は同じ名前で pluginSearch に移行できます。
export default {
search: {
hit: {
minLength: 3,
number: false,
english: true,
hiragana: false,
katakana: true,
kanji: true,
},
},
}import { pluginSearch } from "minista"
export default {
plugins: [
pluginSearch({
hit: {
minLength: 3,
number: false,
english: true,
hiragana: false,
katakana: true,
kanji: true,
},
}),
],
}delivery
Delivery(納品リスト)は廃止されました。include exclude trimTitle sortBy に対応する移行先はありません。
archives で圧縮ファイルを生成していた場合は、ministaのプラグイン pluginArchive に移行してください。
export default {
delivery: {
archives: [{ srcDir: "dist", outName: "dist" }],
},
}import { pluginArchive } from "minista"
export default {
plugins: [
pluginArchive({
archives: [{ srcDir: "dist", outName: "dist" }],
}),
],
}beautify
ministaのプラグイン pluginBeautify のオプションに変更してください。v3の useHtml useAssets は廃止され、対象ファイルは src で指定します。
export default {
beautify: {
useHtml: true,
useAssets: false,
htmlOptions: {},
cssOptions: {},
jsOptions: {},
},
}import { pluginBeautify } from "minista"
export default {
plugins: [
pluginBeautify({
src: ["**/*.{html,css,js}"],
htmlOptions: {},
cssOptions: {},
jsOptions: {},
}),
],
}vite
v4ではministaのコンフィグ自体がViteのコンフィグになったため、vite の中身は直下に移動してください。
export default {
vite: {
server: { port: 3000 },
},
}export default {
server: { port: 3000 },
}Separate asset directories
Viteのデフォルト設定では、すべてのアセットが同じディレクトリに出力されます。CSS・JavaScript・画像・フォントを別々のディレクトリに出力したい場合は、以下のプロパティを設定してください。
- CSS・画像・フォント:
build.rolldownOptions.output.assetFileNames - JavaScript(chunk):
build.rolldownOptions.output.chunkFileNames - JavaScript(entry):
build.rolldownOptions.output.entryFileNames
※これらを設定した場合 build.assetsDir は無視されます。
assetFileNames は拡張子ごとに分けることができます。
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: {
rolldownOptions: {
output: {
assetFileNames,
chunkFileNames: "assets/js/[name].js",
entryFileNames: "assets/js/[name].js",
},
},
},
}Import Paths
v4では機能ごとにインポートパスが分かれています。主な移行先は以下です。
- コンフィグ・プラグイン:
minista - アセット系コンポーネント:
minista/assets - Headコンポーネント:
minista/head
import { Head, Image } from "minista"import { Image } from "minista/assets"
import { Head } from "minista/head"Svgr to Svg
v3のSVGRは、SVGをReactコンポーネントとしてimportしていました。v4では pluginSvg と <Svg> コンポーネントに移行してください。
import Logo from "../assets/logo.svg"
export default function () {
return <Logo className="logo" />
}import { Svg } from "minista/assets"
export default function () {
return <Svg src="/src/assets/logo.svg" className="logo" />
}Icon to Sprite
v3の Icon はv4の Sprite に移行してください。name で指定していたアイコン名は、SVGファイルへのルートパスを src に指定する形へ変わります。
import { Icon } from "minista"
export default function () {
return <Icon name="square" />
}import { Sprite } from "minista/assets"
export default function () {
return <Sprite src="/src/assets/icons/square.svg" />
}Partial Hydration to Island
v3のPartial Hydrationはv4のIslandに移行してください。v3ではコンポーネントのimportパス末尾に ?ph を付与していましたが、v4では通常のimportに戻し、使用箇所に client:* ディレクティブを付与します。
v3のPartial Hydration対象コンポーネントは隔離されていたためpropsを渡せず、named exportにも対応していませんでした。v4のIslandではpropsを渡せるため、必要に応じてコンポーネント設計も見直してください。
import BlockCounter from "../../components/block-counter?ph"
export default function () {
return <BlockCounter /> // You can't pass Props.
}import BlockCounter from "../../components/block-counter"
export default function () {
return <BlockCounter client:load />
}v3のデフォルト設定では、ブラウザでコンポーネントが画面に表示されるとReact Appとして復元されていました。同じタイミングに寄せる場合は client:visible を使用してください。
import BlockCounter from "../../components/block-counter"
export default function () {
return <BlockCounter client:visible />
}