かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Gulp ディレクトリ構造を保ったままリネームしたい

ニッチ過ぎて一体どこに需要があるのか謎すぎるのですが、gulpでディレクトリ構造を保ったままリネームして出力する方法を試していたのでメモです。
stylusで試してますがpugやsass、jsファイルでも同じかと思います。

renameしない場合、パスの/**/の部分のディレクトリは出力される

// gulpfile.js
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( gulp.dest( destPath ) );
});

次のようなファイル構成だったとき

/stylus
  |- main.styl
  |- /page
       |- unique.styl

👇 gulpでコンパイル

/webroot
  |- /css
      |- main.css
      |- /page
           |- unique.css

gulp.src()に渡されるパスの**に該当する部分のディレクトリがそのまま出力されます。

gulp-rename でリネームする場合

引数にリネームされるファイル名をそのまま入れるとディレクトリ構造は維持されない

// gulpfile.js
const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename( 'page.css' ) )
    .pipe( gulp.dest( destPath ) );
});

👇 gulpでコンパイル

/webroot
  |- /css
      |- page.css

gulp.dest で指定されたパス直下にリネームに渡したファイル名でそのまま出力されます。(stylusは2ファイルあるけど同一ファイル名で上書きされるので最終的にcssは1ファイルのみ出力されます)

function か hash を使ってリネームするとディレクトリ構造が維持される

rename via function

const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename(function(path) {
      path.basename = 'page';
      console.log( path.dirname ); // ディレクトリ名が入っている
    }) )
    .pipe( gulp.dest( destPath ) );
});

rename via hash

const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename({
      basename: 'page'
    }) )
    .pipe( gulp.dest( destPath ) );
});

👇 gulpでコンパイル

/webroot
  |- /css
      |- page.css
      |- /page
           |- page.css

gulp.src()に渡されるパスの**に該当する部分のディレクトリ名がfunctionならpath.dirname、hashならdirnameにデフォルトで入っているので、ディレクトリも構造を維持したまま出力されます。

dirname is the relative path from the base directory set by gulp.src to the filename.
gulp.src() uses glob-stream which sets the base to the parent of the first directory glob (*, **, [], or extglob). dirname is the remaining directories or ./ if none. glob-stream versions >= 3.1.0 (used by gulp >= 3.2.2) accept a base option, which can be used to explicitly set the base.
[出典] gulp-rename - npm

 
gulpでディレクトリ構造を維持したままリネームして出力したい場合はgulp-renameでfunctionかhashを使う方法でリネームさせればOKなようです
gulp.src()の第2引数に渡せるbaseオプションの使いみちがいまいち理解できてないので、もっと便利にリネームして出力する方法があるのかもしれません。


Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Gulp Stylusでautoprefixerを使うメモ

nibだけだと今は不要になってるベンダープレフィックスも出力してしまうのでautoprefixerを使って対象ブラウザをコントロールするメモ

node: v9.6.1
npm: v6.1.0

gulp@3.9.1
gulp-stylus@2.7.0
gulp-autoprefixer@2.3.1
nib@1.1.2

install

$ npm install --save-dev gulp-autoprefixer

stylusで使用するならautoprefixer-stylusというパッケージもあるみたいで、中身はautoprefixerを使っているので問題ないと思うのだけれど、最終アップデートが1年前だったのでgulp-autoprefixerを使うことにしました。

gulpfile.js

"use strict";
const gulp    = require('gulp'),
      plumber = require('gulp-plumber'),
      stylus  = require('gulp-stylus'),
      nib     = require('nib'),
      prefix  = require('gulp-autoprefixer');

const devDir = './dev',
      destDir = './webroot';

// task
gulp.task('stylus', function() {
  let files = devDir + '/stylus/main.styl',
      destPath = destDir + '/assets/css';

  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus({
      import: ['nib'],
      use:    [nib()],
      compress: false,
      linenos:  false
    }) )
    .pipe( prefix({
      browsers: ['last 2 versions', '> 1% in JP', 'ie >= 10']
      cascade: false
    }) )
    .pipe( gulp.dest( destPath ) );
});

gulp-autoprefixer browsers オプションで対応ブラウザを指定する

autoprefixerbrowsers オプションで対応させるブラウザを配列で複数指定ができます。
上記の

browsers: ['last 2 versions', '> 1% in JP', 'ie >= 10']

は「最新の2バージョン、日本で1%以上のシェア、IE10以上に対応」という感じです。

対象ブラウザはbrowserl.istでチェックすることが出来ます。(, 区切りで複数条件もチェックできるっぽいです)
オプションの詳細はbrowserslist - Githubで確認ができるのですが、gulp-autoprefixerのバージョンのせいなのかちゃんと調べてはないのですが、last 2 major versionsnot ie <= 8といったオプションだとエラーになってコンパイルできませんでした。

コンパイル結果

Stylus

::selection
  background: #A5EFD2

main
  a
    transition: all .25s
    opacity: 1
    user-select: none
    &:hover
      opacity: .6
      transform: scale(.9, .9)

.flex
  display: flex

1. nibだけ (autoprefixer) 無しの時

::selection {
  background: #a5efd2;
}
main a {
  -webkit-transition: all 0.25s;
  -moz-transition: all 0.25s;
  -o-transition: all 0.25s;
  -ms-transition: all 0.25s;
  transition: all 0.25s;
  opacity: 1;
  -ms-filter: none;
  filter: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
main a:hover {
  opacity: 0.6;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
  filter: alpha(opacity=60);
  -webkit-transform: scale(0.9, 0.9);
  -moz-transform: scale(0.9, 0.9);
  -o-transform: scale(0.9, 0.9);
  -ms-transform: scale(0.9, 0.9);
  transform: scale(0.9, 0.9);
}
.flex {
  display: -webkit-box;
  display: -moz-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: box;
  display: flex;
}

2. autoprefixer使用時

::-moz-selection {
  background: #a5efd2;
}
::selection {
  background: #a5efd2;
}
main a {
  transition: all 0.25s;
  opacity: 1;
  -ms-filter: none;
  filter: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
main a:hover {
  opacity: 0.6;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
  filter: alpha(opacity=60);
  transform: scale(0.9, 0.9);
}
.flex {
  display: -ms-flexbox;
  display: box;
  display: flex;
}

新しく足りてないベンダープレフィックスが追加され、不要なベンダープレフィックスが削除されてスッキリしました!
状況としてはnibで色々ベンダープレフィックスが出力されて、その後でautoprefixerがゴニョゴニョしてるという感じです。display: flexに関してはnibdisplay: box;が出力されてしまい、 次のような

You should write display: flex by final spec instead of display: box

display: boxではなく、display: flexを使ってねというメッセージが出てdisplay: box;は削除されず残ったままになりました。

最近のブラウザだと結構ベンダープレフィックス不要になっていたのですね... 自動コンパイルで放置してたから気づいていませんでした...

gulp-autoprefixerにベンダープレフィックス付きのプロパティのインデントを整形するcascadeオプションがあるのですが、nibでベンダープレフィックスが出力されてしまっていると整形(cascade: true)は効かないようです。
実際に使用する場合はmin化とかしてしまうと思うので、まぁ気にするほどではないと思いますが。(オプション効かない気持ち悪さはあります)
 

autoprefixer-stylus の場合

折角なのでgulp-autoprefixerではなくautoprefixer-stylusも試してみました。

gulpfile.js

"use strict"
const prefix = require('autoprefixer-stylus');

// task
gulp.task('stylus', function() {
  let files = devDir + '/stylus/main.styl',
      destPath = destDir + '/assets/css';

  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus({
      import: ['nib'],
      use: [nib(), prefix({
        browsers: ['last 2 versions', '> 1% in JP', 'ie >= 10'],
        cascade: true
      })],
      compress: false,
      linenos:  false
    }) )
    .pipe( gulp.dest( destPath ) );
});

autoprefixer-stylus の場合stylusのuse内に処理を書けばOK。
同様のstylusをコンパイルしてみます。

コンパイル結果

Stylus

::selection
  background: #A5EFD2

main
  a
    transition: all .25s
    opacity: 1
    user-select: none
    &:hover
      opacity: .6
      transform: scale(.9, .9)

.flex
  display: flex

::-moz-selection {
  background: #a5efd2;
}
::selection {
  background: #a5efd2;
}
main a {
  -webkit-transition: all 0.25s;
  transition: all 0.25s;
  opacity: 1;
  -ms-filter: none;
  -webkit-filter: none;
          filter: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
main a:hover {
  opacity: 0.6;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
  filter: alpha(opacity=60);
  -webkit-transform: scale(0.9, 0.9);
  transform: scale(0.9, 0.9);
}
.flex {
  display: -webkit-box;
  display: -ms-flexbox;
  display: box;
  display: flex;
}

cascadeオプションは効いています。
display: flexに関してはnibで出力されるdisplay: boxのせいで長めのメッセージが表示されて少しびっくりしますが問題なくコンパイルは動作します。
ただ、gulp-autoprefixerと同じブラウザの指定なのですが出力されるベンダープレフィックスに違いが... ξ(Ծ‸Ծ)ξ
(どっちが正しいんだ...
 
個人的にstylus以外にしたときにも使えそうなのでgulpで使用するならgulp-autoprefixerで良いかな〜という気がしました。
nibでdisplay: boxを出力しないオプションが有るのか知りたい...


[参考]

HTML5 & CSS3 デザインレシピ集

HTML5 & CSS3 デザインレシピ集

Gulp4 gulpコマンドでgulpInst.start.apply(gulpInst, toRun); Cannot read property 'apply' of undefinedで動かなくなった

久々にnpm installしてgulpを動かそうとしたところ

$ gulp
[08:46:54] Using gulpfile ~/Documents/local/gulpfile.js
/.nodebrew/node/v6.9.2/lib/node_modules/gulp/bin/gulp.js:129
    gulpInst.start.apply(gulpInst, toRun);
                   ^
TypeError: Cannot read property 'apply' of undefined

というエラーが表示されてgulpが動かなくなってしまいました。
gulpのヴァージョンが4.0になってglobalにインストールされていたgulp-cliのヴァージョンが合わなくてエラーになっているようでした。

npm install gulp@nextとしないとv4は入らないという記事を見たのですが、もうnpm install gulpでv4系が入ってしまうかも知れません...

解決方法 gulp-cli をインストールしなおす

npm i -g gulp-cli

ただ、

グローバルにgulp-cliを入れなくて済みます。
メモ: gulp-cliは、gulpの依存性として、ローカルのnode_modulesにインストールされています。
[出典]: 帰ってきたGulp 4

ということらしいので、globalのgulp-cliは削除してしまってもよいのかも知れません。
(個人的にはnode_modules がすぐ巨大になっていく問題をどうにかして欲しいのん...


[参考]

Webデザイナーの仕事を楽にする! gulpではじめるWeb制作ワークフロー入門

Webデザイナーの仕事を楽にする! gulpではじめるWeb制作ワークフロー入門

👆 2018/5/29 出版のgulpの本。なか見!見た感じだとv4対応っぽい