SymfonyでJS、SCSSをWebpack encoreでビルドするには


Symfonyで、JavascriptやCSSなどフロントエンドのアセットを管理する仕組みとして、以前はAsseticが用意されていましたが、Webpackを利用するWebpack Endoreに置き替わり今時な構成になりました。

Laravelで言うところのElixirにあたります。

Webpack Encoreの利用方法については、公式ドキュメント Managing CSS and JavaScript に一通りの使い方が載っています。
また、Symfonyのでもアプリ symfony/symfony-demo が、Webpack Encoreを使った構成になっているので、こちらのソースを読むほうが解りやすいかもしれません。

この記事では、公式ドキュメントの例、First Example を試してみた手順をまとめいます。
公式ドキュメントでは、nodeのパッケージ管理システムにyarnを使っていますが、私は普段まだnpmを使っているので、npmを利用してパッケージをインストールしています。

プロジェクトの構成

symfony/symfony-demo を参考に、以下のようなディレクトリ構成にします

$ tree -L 2
.
├── app
├── assets
│   ├── css
│   └── js
├── node_modules
├── package.json
├── package-lock.json
├── src
├── tests
├── var
├── vendor
├── web
│   └── build
└── webpack.config.js

追加しているディレクトリ・ファイルは以下のとおりです。

  • assets/js: Javascriptのソースを配置
  • assets/css: CSS(SCSS)のソースを配置
  • node_modules: ローカルインストールされるnpmパッケージを保存、npm installコマンド実行で自動生成される
  • webpack.config.js: webpackの設定
  • package.json: npmのプロジェクト設定ファイル
  • pckage-lock.json: 依存npmパッケージ ロックファイル
  • web/build Webpackを実行して、バンドルされたファイルが出力されるディレクトリ

プロジェクトの準備

Symfonyのプロジェクトを用意します

$ composer create-project symfony/framework-standard-edition symfony-webpack-encore
$ cd symfony-webpack-encore

アセットのソースを配置するディレクトリを作成します

$ mkdir assets
$ mkdir assets/js
$ mkdir assets/css

Webpack Encoreのインストール

Webpack Encoreはnpmパッケージとして提供されています。
npm initしてから、@symfony/webpack-encoreをインストールします。

$ npm init -y
$ npm install @symfony/webpack-encore --save-dev

package.jsonpackage-lock.jsonファイルが生成されます。

補足: npmを利用する場合は、npm initを実行してpackage.jsonを生成しておく必要があります
package.jsonが無いとencoreコマンド実行時にCannot determine webpack context.とエラーになります。

webpack.config.jsの作成

Webpackの設定ファイル webpack.config.js を作成します

以下は、First ExampleのConfiguring Encore/Webpackのコードからスタイルシート関連を削除したものです。

// webpack.config.js
var Encore = require('@symfony/webpack-encore');

Encore
    // バンドルされたアセットが出力されるディレクトリ
    .setOutputPath('web/build/')

    // `outputPath`の公開版パス (プロジェクトのドキュメントルートからの相対パスを指定)
    .setPublicPath('/build')

    // ビルドする前に`outputPath`に指定したディレクトリを空にする
    .cleanupOutputBeforeBuild()

    // エントリファイルの指定、バンドルされたファイルは`web/build/app.js`として出力される
    .addEntry('app', './assets/js/main.js')

    // jQueryをProvidePluginでグローバルに利用できるようにするかどうか
    .autoProvidejQuery()

    .enableSourceMaps(!Encore.isProduction())
;

// export the final configuration
module.exports = Encore.getWebpackConfig();

Webpackをそのまま使うより簡潔に記述できますが、Webpackそのものは理解をしていないと設定は難しそうです。

サンプル実装

Requiring JavaScript Modulesに記述されているコードで動作を確認してみます

バンドルしたファイルを読み込むテンプレート

デフォルトのレイアウトテンプレートbase.html.twigでバンドルしたcss、jsを読み込むようにします

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>{% block title %}Welcome!{% endblock %}</title>
        <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
    </head>
    <body>
        {% block body %}{% endblock %}
        <script src="{{ asset('build/app.js') }}"></script>
    </body>
</html>

JSモジュールとエントリーファイル

文字列を返すだけのシンプルな関数を含むモジュールを記述し、CommonJSスタイルでexportします

// assets/js/greet.js
module.exports = function(name) {
    return `Yo yo ${name} - welcome to Encore!`;
};

このgreet.jsを利用するmain.jsを以下のように記述します

// assets/js/main.js

// loads the jquery package from node_modules
var $ = require('jquery');

// import the function from greet.js (the .js extension is optional)
// ./ (or ../) means to look for a local file
var greet = require('./greet');

$(document).ready(function() {
    $('h1').html(greet('john'));
});

DOMへのレンダリングはjQueryを利用していますので、npmでjqueryをインストールしておきます

$ npm install jquery --save-dev

ビルドして動作確認

以下のコマンドでビルドします。

$ ./node_modules/.bin/encore dev
Running webpack ...

 DONE  Compiled successfully in 1006ms                                                             10:12:35

 I  2 files written to web/build

サーバーを起動

$ bin/console server:run

ブラウザで表示してみると、いつものスタートアップページの上部にJavascriptで追加した「Yo yo john – welcome to Encore!」が表示されたことを確認できます。

SCSSのコンパイル

次にSCSSのコンパイル設定も確認しておきます。

公式ドキュメント CSS Preprocessors: Sass, LESS, etc.のUsing Sassを参考にWebpackの環境をセットアップします。

パッケージを追加

Webpackのsass-loaderをインストールします

$ npm install sass-loader node-sass --save-dev

webpack.config.js

webpack.config.jssass-loaderの有効化と、SCSSのエントリーファイルの指定を追加します

// webpack.config.js
var Encore = require('@symfony/webpack-encore');

Encore
    // ...

    // CSSのエントリーファイル、コンパイルし`web/build/main.css`ファイルとして出力
    .addStyleEntry('main', './assets/css/main.scss')

    // sass-loaderを有効化
    .enableSassLoader()

    //...
;

サンプル

h1のテキストを赤くするだけの簡単なSCSSファイルで確認してみます

// assets/css/main.scss
$header-color: red !default;

h1 {
  color: $header-color;
}

テンプレートにbuild/main.cssの読み込みを追加

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <!-- ... -->
        <link rel="stylesheet" href="{{ asset('build/main.css') }}">
    </head>
    <!-- ... -->
</html>

動作確認

再度ビルドし直してブラウザで確認すると以下のように、h1の色が赤く表示されました

TypeScriptのコンパイル

おまけですが、TypeScriptの導入も確認してみます。

公式ドキュメントのセットアップ手順は、Enabling TypeScript (ts-loader)に記述されています。

パッケージの追加

typescriptts-loaderをインストールします

$ npm install typescript ts-loader --save-dev

webpack.config.jsの修正

webpack.config.jsmain.jsのエントリーをmain.tsに変更し、enableTypeScriptLoaderでTypeScriptのコンパイルを有効化します。

// webpack.config.js
// ...
Encore
    // ...
    .addEntry('app', './assets/js/main.ts')
    .enableTypeScriptLoader()

TypeScirptのコンパイルオプションの設定ファイルtsconfig.jsonを以下のように作成します

// tsconfig.json
{
    "compilerOptions": {
        "noImplicitAny": true,
        "module": "commonjs",
        "target": "es5",
        "jsx": "react",
        "lib" : ["es2015", "es2015.iterable", "dom"]
    },
    "include": [
        "./assets/js/**/*"
    ]
}

サンプル実装をTypeScriptで書き換え

サンプルはjQueryを利用しているので、TypeScriptの型定義をインストールする必要があります

$ npm install jquery @types/jquery --save-dev

jsは拡張子をtsに変更して、以下のようにTypeScriptに書き換えます

// assets/js/main.ts

import * as $ from 'jquery';
import greet from './greet';

$(document).ready(function() {
    $('h1').html(greet('john'));
});
// assets/js/greet.ts
export default function(name:string) {
    return `Yo yo ${name} - welcome to Encore!`;
};

動作確認

encoreコマンドを実行し直します

$ ./node_modules/.bin/encore dev
Running webpack ...

 DONE  Compiled successfully in 2149ms                                                             23:58:21

 I  3 files written to web/build

動作は同じです

, ,