svelte-preprocess 導入ガイド:SCSS・PostCSS・TypeScript を安全に共存
Svelte で本格的な Web アプリケーションを開発する際、TypeScript で型安全性を確保しながら、SCSS で便利なスタイル記法を使いたい、さらに PostCSS でベンダープレフィックスを自動追加したいといったニーズは非常に多いです。しかし、これらの複数のプリプロセッサを組み合わせると、設定の競合やビルドエラーに悩まされることも少なくありません。
そこで活躍するのが svelte-preprocess です。このライブラリを使えば、SCSS・PostCSS・TypeScript といった異なるプリプロセッサを 1 つの設定ファイルで管理でき、安全に共存させることができます。本記事では、svelte-preprocess の基本から実践的な設定まで、初心者の方にもわかりやすく解説していきます。
背景
Svelte における複数プリプロセッサの必要性
Svelte は .svelte という単一ファイルコンポーネント形式を採用しており、1 つのファイルに HTML・CSS・JavaScript を記述できます。しかし、実務では以下のような要望が出てきます。
- TypeScript: 型チェックによる安全なコード記述
- SCSS: 変数・ネスト・mixin などの便利な CSS 記法
- PostCSS: Autoprefixer によるベンダープレフィックス自動追加や、CSS の最適化
これらを個別に設定しようとすると、それぞれのプラグインを導入し、ビルドツール(Vite や Rollup)ごとに異なる設定を記述する必要があります。設定ファイルが複雑化し、プリプロセッサ間で処理順序が競合するリスクも高まります。
svelte-preprocess の役割
svelte-preprocess は、複数のプリプロセッサを統合管理するためのライブラリです。以下の図のように、.svelte ファイルが Svelte コンパイラに渡される前に、各種プリプロセッサを順次適用してくれます。
次の図は、svelte-preprocess がどのように各プリプロセッサを統合するかを示しています。
mermaidflowchart TB
svelteFile[".svelte ファイル"]
preprocessor["svelte-preprocess"]
typescript["TypeScript 変換"]
scss["SCSS → CSS 変換"]
postcss["PostCSS 処理"]
compiler["Svelte コンパイラ"]
output["最終的な JS/CSS"]
svelteFile --> preprocessor
preprocessor --> typescript
typescript --> scss
scss --> postcss
postcss --> compiler
compiler --> output
図で理解できる要点:
.svelteファイルは svelte-preprocess を経由して各種変換が行われる- TypeScript → SCSS → PostCSS の順で処理され、最後に Svelte コンパイラに渡される
- すべての処理が 1 つのプリプロセッサで統合管理される
このように、svelte-preprocess を導入することで、複数のプリプロセッサを 1 つの設定で管理でき、処理順序も明確になります。
課題
複数プリプロセッサ利用時の典型的な問題
svelte-preprocess を使わずに複数のプリプロセッサを導入すると、以下のような問題に直面します。
課題 1: 設定ファイルの複雑化
各プリプロセッサを個別に設定すると、vite.config.js や rollup.config.js が肥大化します。
typescript// vite.config.js の例(svelte-preprocess なしの場合)
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import sveltePreprocessTypescript from 'svelte-preprocess-typescript';
import sveltePreprocessScss from 'svelte-preprocess-scss';
import autoprefixer from 'autoprefixer';
export default defineConfig({
plugins: [
svelte({
preprocess: [
sveltePreprocessTypescript(),
sveltePreprocessScss(),
// PostCSS の設定をどこに入れる?
],
}),
],
});
上記のコードでは、TypeScript と SCSS の設定は別々のプラグインで行っていますが、PostCSS をどこに組み込むかが不明確です。さらに、各プラグインのバージョン互換性も個別に管理する必要があり、保守性が低下します。
課題 2: 処理順序の競合
複数のプリプロセッサが同じ <style> タグや <script> タグを処理しようとすると、処理順序が不明確になり、以下のようなエラーが発生することがあります。
javascriptError: Unexpected token (1:0)
SyntaxError: Unexpected token 'export'
エラーコード: SyntaxError
発生条件: TypeScript が SCSS より後に処理されるなど、処理順序が逆転した場合
エラーメッセージ:
vbnetSyntaxError: Unexpected token 'export'
at Function.Module._load (internal/modules/cjs/loader.js:883:27)
このエラーは、TypeScript のコードが先に SCSS プロセッサに渡されてしまい、TypeScript の構文が理解できずにエラーとなるケースです。
課題 3: ビルドツールごとの設定差異
Vite・Rollup・Webpack など、ビルドツールごとにプリプロセッサの設定方法が異なります。プロジェクトを移行する際や、複数のビルドツールを併用する場合に、設定を書き直す必要が生じます。
次の図は、課題の関係性を示しています。
mermaidflowchart LR
A["複数プリプロセッサ導入"]
B["設定ファイル肥大化"]
C["処理順序の競合"]
D["ビルドツール依存"]
E["保守性の低下"]
A --> B
A --> C
A --> D
B --> E
C --> E
D --> E
図で理解できる要点:
- 複数プリプロセッサの導入が、3 つの主要な課題を引き起こす
- これらの課題はすべて、最終的に保守性の低下につながる
これらの課題を解決するために、svelte-preprocess が開発されました。
解決策
svelte-preprocess による統合管理
svelte-preprocess は、複数のプリプロセッサを 1 つの設定で管理できるライブラリです。以下の特徴があります。
| # | 特徴 | 説明 |
|---|---|---|
| 1 | 統合設定 | TypeScript・SCSS・PostCSS などを 1 つの設定オブジェクトで管理 |
| 2 | 自動検出 | lang="ts" や lang="scss" を自動で検出し、適切なプリプロセッサを適用 |
| 3 | 処理順序の明確化 | 内部で最適な処理順序を自動調整 |
| 4 | ビルドツール非依存 | Vite・Rollup・Webpack すべてで同じ設定を利用可能 |
インストール手順
まず、必要なパッケージをインストールします。
ステップ 1: svelte-preprocess のインストール
bashyarn add -D svelte-preprocess
このコマンドで、svelte-preprocess 本体を開発依存としてインストールします。
ステップ 2: プリプロセッサ本体のインストール
次に、使用したい各プリプロセッサをインストールします。
bashyarn add -D typescript sass postcss autoprefixer
各パッケージの役割は以下の通りです。
| # | パッケージ | 役割 |
|---|---|---|
| 1 | typescript | TypeScript コンパイラ本体 |
| 2 | sass | SCSS を CSS に変換するコンパイラ(Dart Sass) |
| 3 | postcss | CSS を変換・最適化するツール |
| 4 | autoprefixer | PostCSS プラグイン。ベンダープレフィックスを自動追加 |
ステップ 3: 設定ファイルの作成
Vite を使用している場合の設定例を見てみましょう。
typescript// vite.config.ts
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
上記は Vite と Svelte プラグインのインポート部分です。次に、svelte-preprocess をインポートします。
typescript// svelte-preprocess のインポート
import sveltePreprocess from 'svelte-preprocess';
続いて、Vite の設定を記述します。
typescript// Vite 設定の定義
export default defineConfig({
plugins: [
svelte({
preprocess: sveltePreprocess({
// TypeScript の設定
typescript: {
tsconfigFile: './tsconfig.json',
},
// SCSS の設定
scss: {
// グローバル変数ファイルを自動インポート
prependData: `@import 'src/styles/variables.scss';`,
},
// PostCSS の設定
postcss: {
plugins: [require('autoprefixer')()],
},
}),
}),
],
});
この設定により、以下が実現されます。
- TypeScript:
tsconfig.jsonの設定に基づいて型チェック - SCSS: すべての
.svelteファイルでvariables.scssを自動インポート - PostCSS: Autoprefixer によるベンダープレフィックス自動追加
ステップ 4: TypeScript 設定ファイルの作成
TypeScript を使用する場合、tsconfig.json も作成します。
json{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
この設定により、最新の JavaScript 機能を使いながら、厳格な型チェックが行われます。
処理フローの明確化
svelte-preprocess を導入すると、以下のように処理フローが明確になります。
mermaidsequenceDiagram
participant SvelteFile as .svelte ファイル
participant Preprocess as svelte-preprocess
participant TS as TypeScript
participant SCSS as Sass
participant PostCSS as PostCSS
participant Compiler as Svelte コンパイラ
SvelteFile->>Preprocess: ファイル読み込み
Preprocess->>Preprocess: lang 属性を検出
alt lang="ts" の場合
Preprocess->>TS: TypeScript 変換
TS-->>Preprocess: JavaScript 返却
end
alt lang="scss" の場合
Preprocess->>SCSS: SCSS → CSS 変換
SCSS-->>Preprocess: CSS 返却
end
Preprocess->>PostCSS: CSS 最適化
PostCSS-->>Preprocess: 最適化 CSS 返却
Preprocess->>Compiler: 変換済みコード
Compiler-->>SvelteFile: 最終的な出力
図で理解できる要点:
- svelte-preprocess が
lang属性を自動検出し、適切なプリプロセッサに振り分ける - 各プリプロセッサは順次処理され、最後に Svelte コンパイラに渡される
- 開発者は
lang属性を指定するだけで、すべての処理が自動化される
具体例
実践的な Svelte コンポーネントの作成
svelte-preprocess を導入した環境で、TypeScript と SCSS を活用した実践的なコンポーネントを作成してみましょう。
グローバル変数ファイルの作成
まず、SCSS のグローバル変数ファイルを作成します。
scss// src/styles/variables.scss
// カラーパレット
$primary-color: #ff3e00;
$secondary-color: #676778;
$background-color: #ffffff;
$text-color: #333333;
// ブレークポイント
$breakpoint-mobile: 768px;
$breakpoint-tablet: 1024px;
// スペーシング
$spacing-small: 8px;
$spacing-medium: 16px;
$spacing-large: 24px;
このファイルには、プロジェクト全体で使用する色・ブレークポイント・スペーシングの変数を定義します。
TypeScript 型定義の作成
次に、コンポーネントで使用する型を定義します。
typescript// src/types/card.ts
export interface CardProps {
title: string;
description: string;
imageUrl?: string;
tags?: string[];
onClickHandler?: () => void;
}
CardProps インターフェースで、カードコンポーネントのプロパティの型を定義しています。? を付けることで、オプショナルなプロパティを表現できます。
Svelte コンポーネントの作成
それでは、TypeScript と SCSS を組み合わせた Svelte コンポーネントを作成しましょう。
ステップ 1: script タグの記述(TypeScript)
svelte<!-- src/components/Card.svelte -->
<script lang="ts">
// 型定義のインポート
import type { CardProps } from '../types/card';
// プロパティの定義(型安全)
export let title: string;
export let description: string;
export let imageUrl: string | undefined = undefined;
export let tags: string[] = [];
export let onClickHandler: (() => void) | undefined = undefined;
// ローカル状態の管理
let isHovered: boolean = false;
lang="ts" を指定することで、svelte-preprocess が自動的に TypeScript として処理します。型アノテーションにより、プロパティの型が明確になり、誤った型の値が渡された場合は開発時にエラーが検出されます。
ステップ 2: イベントハンドラの定義
svelte // マウスイベントハンドラ
const handleMouseEnter = (): void => {
isHovered = true;
};
const handleMouseLeave = (): void => {
isHovered = false;
};
// クリックイベントハンドラ
const handleClick = (): void => {
if (onClickHandler) {
onClickHandler();
}
};
</script>
イベントハンドラも TypeScript の恩恵を受けて、型安全に記述できます。戻り値の型を void と明示することで、意図しない返り値を防げます。
ステップ 3: マークアップの記述
svelte<!-- カードコンポーネントのマークアップ -->
<div
class="card"
class:hovered={isHovered}
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
on:click={handleClick}
role="button"
tabindex="0"
on:keydown={(e) => e.key === 'Enter' && handleClick()}
>
{#if imageUrl}
<div class="card__image">
<img src={imageUrl} alt={title} />
</div>
{/if}
<div class="card__content">
<h3 class="card__title">{title}</h3>
<p class="card__description">{description}</p>
{#if tags.length > 0}
<div class="card__tags">
{#each tags as tag}
<span class="card__tag">{tag}</span>
{/each}
</div>
{/if}
</div>
</div>
Svelte の制御フロー構文({#if}、{#each})を使って、条件付きレンダリングとリストレンダリングを実現しています。
ステップ 4: スタイルの記述(SCSS + PostCSS)
svelte<style lang="scss">
// variables.scss は自動インポートされる
.card {
background-color: $background-color;
border-radius: $spacing-small;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
// ホバー時のスタイル(クラスバインディング)
&.hovered {
transform: translateY(-4px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
lang="scss" を指定することで、SCSS の記法が使えます。$background-color などの変数は、先ほど vite.config.ts で設定した prependData により自動インポートされています。
ステップ 5: ネストとレスポンシブデザイン
svelte // 画像エリア
&__image {
width: 100%;
height: 200px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
// ホバー時の画像拡大
.hovered & img {
transform: scale(1.05);
}
}
// コンテンツエリア
&__content {
padding: $spacing-medium;
}
// タイトル
&__title {
color: $primary-color;
font-size: 1.25rem;
font-weight: bold;
margin: 0 0 $spacing-small 0;
}
// 説明文
&__description {
color: $text-color;
font-size: 0.9rem;
line-height: 1.6;
margin: 0 0 $spacing-medium 0;
}
// タグエリア
&__tags {
display: flex;
flex-wrap: wrap;
gap: $spacing-small;
}
// 個別タグ
&__tag {
background-color: $secondary-color;
color: $background-color;
padding: 4px $spacing-small;
border-radius: 4px;
font-size: 0.75rem;
}
// レスポンシブデザイン
@media (max-width: $breakpoint-mobile) {
&__image {
height: 150px;
}
&__title {
font-size: 1.1rem;
}
&__description {
font-size: 0.85rem;
}
}
}
</style>
SCSS のネスト記法(&)を使うことで、BEM のようなクラス命名規則を読みやすく記述できます。また、メディアクエリもネスト内に記述でき、コンポーネント単位でのスタイル管理が容易になります。
PostCSS の Autoprefixer が有効になっているため、transform、transition、flex などのプロパティには自動的にベンダープレフィックスが追加されます。
コンポーネントの使用例
作成したコンポーネントを、別のファイルから使用してみましょう。
svelte<!-- src/routes/+page.svelte -->
<script lang="ts">
import Card from '../components/Card.svelte';
// カードデータ(型安全)
const cards = [
{
title: 'svelte-preprocess 導入',
description: 'TypeScript と SCSS を安全に共存させる方法',
imageUrl: '/images/svelte.jpg',
tags: ['Svelte', 'TypeScript', 'SCSS'],
onClickHandler: () => console.log('Card 1 clicked'),
},
{
title: 'PostCSS の活用',
description: 'Autoprefixer でベンダープレフィックスを自動追加',
tags: ['PostCSS', 'CSS'],
onClickHandler: () => console.log('Card 2 clicked'),
},
];
</script>
<main>
<h1>技術記事一覧</h1>
<div class="card-grid">
{#each cards as card}
<Card {...card} />
{/each}
</div>
</main>
<style lang="scss">
main {
max-width: 1200px;
margin: 0 auto;
padding: $spacing-large;
}
h1 {
color: $primary-color;
margin-bottom: $spacing-large;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: $spacing-large;
}
</style>
スプレッド構文({...card})を使うことで、オブジェクトのプロパティをプロパティとして一括で渡せます。TypeScript の型チェックにより、必須プロパティが不足している場合は開発時にエラーが表示されます。
エラー時のデバッグ方法
svelte-preprocess を使用中にエラーが発生した場合の対処法を見ていきましょう。
エラー 1: TypeScript コンパイルエラー
エラーコード: TS2322
エラーメッセージ:
pythonType 'number' is not assignable to type 'string'.
発生条件: 型が一致しないプロパティを渡した場合
解決方法:
- エラーメッセージで指摘された行を確認する
- 渡しているプロパティの型を確認する
- 必要に応じて型を修正、または型変換を行う
typescript// 誤った例
export let title: string;
title = 123; // エラー: number は string に代入できない
// 正しい例
export let title: string;
title = '123'; // OK
// または
title = String(123); // 型変換
エラー 2: SCSS コンパイルエラー
エラーコード: SassError
エラーメッセージ:
yamlSassError: Undefined variable.
╷
15 │ color: $undefined-color;
│ ^^^^^^^^^^^^^^^^
╵
発生条件: 定義されていない SCSS 変数を使用した場合
解決方法:
variables.scssに該当の変数が定義されているか確認するvite.config.tsのprependDataの設定が正しいか確認する- 変数名のスペルミスがないか確認する
scss// variables.scss に追加
$undefined-color: #ff0000;
エラー 3: PostCSS プラグインエラー
エラーコード: Error
エラーメッセージ:
arduinoError: Cannot find module 'autoprefixer'
発生条件: PostCSS プラグインがインストールされていない場合
解決方法:
- 必要なパッケージをインストールする
bashyarn add -D autoprefixer postcss
vite.config.tsでプラグインを正しくインポートしているか確認する
typescript// 正しいインポート方法
import autoprefixer from 'autoprefixer';
// 設定
postcss: {
plugins: [autoprefixer()],
}
パフォーマンス最適化
svelte-preprocess を使用する際のパフォーマンス最適化のポイントをご紹介します。
最適化 1: 開発時の型チェック無効化
開発時に TypeScript の型チェックが遅い場合、以下のように設定できます。
typescript// vite.config.ts
export default defineConfig({
plugins: [
svelte({
preprocess: sveltePreprocess({
typescript: {
// 開発時は型チェックをスキップし、ビルド時のみ実行
compilerOptions: {
noEmit: true,
},
},
}),
}),
],
});
最適化 2: SCSS のソースマップ設定
本番環境ではソースマップを無効化してファイルサイズを削減できます。
typescript// vite.config.ts
export default defineConfig({
plugins: [
svelte({
preprocess: sveltePreprocess({
scss: {
// 本番環境ではソースマップを無効化
sourceMap: process.env.NODE_ENV !== 'production',
},
}),
}),
],
});
まとめ
本記事では、svelte-preprocess を使って TypeScript・SCSS・PostCSS を安全に共存させる方法を解説しました。
svelte-preprocess を導入することで、以下のメリットが得られます。
技術面のメリット:
- 複数のプリプロセッサを 1 つの設定ファイルで統合管理できる
- 処理順序が自動的に最適化され、競合リスクが低減される
- TypeScript による型安全性と、SCSS の便利な記法を同時に活用できる
- PostCSS により、ベンダープレフィックスの自動追加など CSS の最適化が自動化される
開発体験のメリット:
- ビルドツールを変更しても、同じ設定を使い回せる
lang属性を指定するだけで、適切なプリプロセッサが自動選択される- 設定ファイルがシンプルになり、保守性が向上する
実践のポイント:
- グローバル変数ファイルを
prependDataで自動インポートすることで、すべてのコンポーネントで共通の変数が使える - TypeScript の型定義を活用し、プロパティの型安全性を確保する
- SCSS のネスト記法で、BEM などのクラス命名規則を読みやすく記述できる
- エラー発生時は、エラーコードとメッセージから原因を特定し、段階的に解決する
svelte-preprocess は、Svelte プロジェクトで複数のプリプロセッサを扱う際の標準的なソリューションとなっています。本記事で紹介した設定方法を参考に、型安全で保守性の高い Svelte アプリケーション開発を始めてみてください。
プロジェクトの規模が大きくなるほど、統合的なプリプロセッサ管理の重要性は高まります。ぜひ、svelte-preprocess を活用して、快適な開発体験を手に入れてください。
関連リンク
articlesvelte-preprocess 導入ガイド:SCSS・PostCSS・TypeScript を安全に共存
articleSvelteKit アダプタ比較:Node/Vercel/Cloudflare/Netlify の速度と制約
articleSvelte ストアエラー「store is not a function」を解決:writable/derived の落とし穴
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleSvelteKit 本番運用チェックリスト:CSP/SRI/Cache-Control/Headers 総点検
articleSvelte フォーム体験設計:Optimistic UI/エラー復旧/再送戦略の型
articleDeno/Bun/Node のランタイムで共通動く Zod 環境のセットアップ
articleFFmpeg マッピング完全攻略:-map/-disposition/-metadata の黄金レシピ
articleYarn PnP で「モジュールが見つからない」時の解決大全:packageExtensions/patch で対処
articleESLint no-restricted-* 活用レシピ集:API 禁止・依存制限・危険パターン封じ込め
articleWeb Components のポリフィル戦略:@webcomponents 系を最小限で入れる判断基準
articleDify ワークフロー定型 30:分岐・並列・リトライ・サーキットブレーカの型
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来