LoginSignup
29
39

More than 3 years have passed since last update.

EJSでルートへのパス(相対、絶対)を取得して変数化する

Last updated at Posted at 2016-06-16

※ 2019年5月19日 EJSで<%= filename %>でパスの取得ができなくなっているので、gulp-dataを使って、gulpfile.js内で取得する方法に変更しました。

EJSではインクルードするパスを相対パスで指定する必要があります。設定ミスを防ぐために、メタタグ内のURLなども自動化したいところです。

開発環境はmacOS High Sierra 10.13.6、Node.js 9.4.0、gulp-ejs 4.0.0で確認しています。

gulpfile

const ejs = require('gulp-ejs');
const fs = require('fs');
const data = require('gulp-data');
const rename = require('gulp-rename');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');

/**
 * 開発用ディレクトリ
 */
const src = {
  root: 'src/',
  ejs: ['src/**/*.ejs', '!src/**/_*.ejs'],
  data: 'src/_data/',
};

/**
 * テスト用ディレクトリ
 */
const dest = {
  root: 'htdocs/',
};

/**
 * EJSをHTMLにコンパイルします。
 */
gulp.task('ejs', () => {
  return gulp
    .src(src.ejs)
    .pipe(
      data(file => {
        const absolutePath = `/${file.path
          .split(src.root)
          [file.path.split(src.root).length - 1].replace('.ejs', '.html')
          .replace(/index\.html$/, '')}`;
        const relativePath = '../'.repeat([absolutePath.split('/').length - 2]);
        return {
          absolutePath,
          relativePath,
        };
      }),
    )
    .pipe(
      ejs({
        site: JSON.parse(fs.readFileSync(`${src.data}site.json`)),
      }),
    )
    .pipe(rename({ extname: '.html' }))
    .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
    .pipe(gulp.dest(dest.root));
});

site変数

サイト共通で使う変数を/src/_data/site.jsonに定義します。この変数はgulpfile.jsを介して、すべてのEJSファイル内でsite.nameのように呼び出すことができます。

{
  "name": "Site Title",
  "description": "Site Description",
  "keywords": "Site Keyword1, Site Keyword2",
  "rootUrl": "http://example.com",
  "ogpImage": "http://example.com/ogp-image.jpg",
  "facebookAdmins": "",
  "facebookAppId": "",
  "twitterCard": "summary_large_image",
  "twitterSite": "@SiteAccount"
}

製品リストのような別の変数を定義したい場合は、jsonファイル(/src/_data/products.json)を作成して、gulpfile.jsで以下のように読み込んでください。

// 変更前
.pipe(
  ejs(
    {
      site: JSON.parse(fs.readFileSync(`${src.data}site.json`)),
    },
    {},
    { ext: '.html' },
  ),
)

<%= products.items %>のように呼び出すことができます。

// 変更後
.pipe(
  ejs(
    {
      site: JSON.parse(fs.readFileSync(`${src.data}site.json`)),
      products: JSON.parse(fs.readFileSync(`${src.data}products.json`)),
    },
    {},
    { ext: '.html' },
  ),
)

absolutePathrelativePath

gulpfile.jsでfsパッケージを使用して、そのページのルート相対パスをabsolutePathに、ルートまでの相対パスをrelativePathに格納しています。この変数は、すべてのEJSファイル内で<%= absolutePath %>のように呼び出せます。

index.htmlは削除され、以下のように出力されます。

  • /index.ejsで使用した場合
    • <%= absolutePath %> => /
    • <%= relativePath %> => 出力なし
  • /products/index.ejsで使用した場合
    • <%= absolutePath %> => /products/
    • <%= relativePath %> => ../
  • /products/product.ejsで使用した場合
    • <%= absolutePath %> => /products/product.html
    • <%= relativePath %> => ../

site.rootUrlと組み合わせることで、ドメインを含んだ絶対パスを出力することもできます。

<%= site.rootUrl %><%= absolutePath %>

メタタグの自動出力

共通のデータやルート相対パスなどは出力できるようになったので、メタタグも自動で生成することができます。

例えば、以下のようにindex.ejsを作成します。include()の第二引数にpage変数を渡して、ページごとの設定をインクルード先(src/_partial/_head.ejs)で使えるようにしておきます。

<%
var page = {
  title: "",
  description: "",
  ogpType: "website",
  ogpImage: "",
  // Do not change.
  absolutePath: absolutePath,
  relativePath: relativePath
};
-%>
<%- include(page.relativePath + '_partial/_head', {page: page}); %>

_head.ejsでは以下のように、page.titleを設定していればページタイトルとサイトタイトルを、page.titleがなければサイトタイトルを生成のように、テンプレートを作れます。

<% if(page.title) { %><title><%= page.title %> | <%= site.name %></title><% } else { %><title><%= site.name %></title><% } %>
29
39
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
39