かもメモ

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

gulp サーバー(gulp-connect)で自動的にブラウザを開きたい

gulpでサイト作る時のローカルサーバーに長らくgulp-webserverを使っていたのですが、CSSの変更時にhotreloadが上手く動作せず、リロードさせる関数も無いっぽかったので、reload関数が有り任意のタイミングでリロードさせることができるgulp-connectに乗り換えました。

gulp watch開始時に自動的にブラウザを開いてほしかったのですが、gulp-connectにはブラウザを開くオプションが無いようだったので、自動的に開くようにしてみました。

open のインストール

ブラウザを開く為にopenを使用します。
gulp-connectopenを使用していないので別途インストールが必要です。

$ yarn add -D open

gulp watch実行時に自動的にloaclhostをブラウザで開くようにする

常に新規ブラウザが開くのもウザイので、NODE_SERVER=1 オプションが有る時だけブラウザで開くようにしたいと思います。

gulpfile.js

const gulp    = require('gulp'),
      connect = require('gulp-connect'),
      open    = require('open');

const webroot = 'app/webroot';

// Server
const server = (cb) => {
  const openFlg = process.env.NODE_SERVER? !!(process.env.NODE_SERVER - 0) : false;
  const webserver = connect.server({
    root: webroot,
    livereload: true,
    port: 3000
  });
  if( openFlg ) {
    open("http" + (webserver.https? 's':'') + "://" + webserver.host + ":" + webserver.port" );
  }
  cb();
};

gulp.task( 'default', gulp.series(server, {tasks...}) );

httpshostなどのgulp-connentのオプションにアクセスする為には一度connect.server()したものを変数に入れ、そこからアクセスする必要がありました。

新規ウィンドウを開くdevというコマンドと、デフォルトのタスクを実行するだけのwatchというコマンドを作成します。 package.json

"scripts": {
  "dev": "NODE_SERVER=1 ./node_modules/.bin/gulp default",
  "watch": "./node_modules/.bin/gulp default"
}

新規ウィンドウを開いてデフォルトのwatchタスクなどを実行

$ yarn dev  

これで、localhostを新規ウィンドウで自動的に開くことができるようになりました!
open使えばgulpに関わらず開発時に自動的にブラウザ開くってのできそう。


[参考]

コネクト

コネクト

gulp4 watch終了したら The following tasks did not complete ってエラーが表示される件

昔作ったgulpでpugとstylusコンパイルしてるだけのLPを更新で弄っていました。
gulpでwatchしてたプロセスを終了したら

[23:55:48] The following tasks did not complete: default, watchHTML, watchCSS
[23:55:48] Did you forget to signal async completion?

ってエラーが表示されました。
タスクの処理そのものは完了しているので実害はないのですが、キモチワルイ...

gulp4 では明示的にタスクの終了を返してあげないとダメっぽい

When a stream, promise, event emitter, child process, or observable is returned from a task, the success or error informs gulp whether to continue or end. If a task errors, gulp will end immediately and show that error.
出典: Async Completion · gulp.js

タスク内でstream, promise, event emitter, child process, observableを返さないとタスクの終了が分らずエラーになるという事のようです。

Using an error-first callback
If nothing is returned from your task, you must use the error-first callback to signal completion. The callback will be passed to your task as the only argument - named cb() in the examples below.
出典: Async Completion - Using an error-first callback · gulp.js

要はタスク内でreturn gulp.src()みたいにstreamを返さないかったりするようなタスクは、タスクに渡される引数のコールバックをタスク内で実行してタスクの終了を明示的にする必要があったっぽい。

// gulpfile.js
// 略

// --------------------------------
// Server
// --------------------------------
const server = (cb) => {
  // stream を return しているものはOK
  return gulp.src('webroot')
    .pipe(webserver({
      port: 3000,
      livereload: true
    }));
};

// --------------------------------
// Watch
// --------------------------------
const watchHTML = (cb) => {
  gulp.watch([
    `${devDir}/pug/**/*.pug`
  ], buildHTML );
  cb(); // 明示的に終了を通知
};

const watchCSS = (cb) => {
  gulp.watch([
    `${devDir}/style/**/*.styl`
  ], gulp.series(buildCss, cssMinify));
  cb();  // 明示的に終了を通知
};

// --------------------------------
// Default
// --------------------------------
gulp.task( 'default', gulp.series('server', gulp.parallel(watchHTML, watchCSS)) );

gulp.watch()streamを返さないっぽいのでreturn gulp.watch()~してもダメでした。(監視してから終了してないですもんね...)
なので関数に渡される引数cb(名前は何でもいい)を実行するように修正したらwatch終了時にThe following tasks did not complete:エラーは出なくなりました。
(・ω<) 解🌟決 (横ピース)

動いて入るもののエラーが出てしまうのはキモチワルイので原因も解ってよかったです。
動いてるからいいやってってするのは良くないですよね!


[参考]

シービージャパン タンブラー レッド 375ml 真空2層構造

シービージャパン タンブラー レッド 375ml 真空2層構造

npm install不要なnode簡単なサーバーつくってみた。

npm installせずにHTML表示できるnodeのサーバーを作ってみました。

server.js

const http = require('http');
const fs   = require('fs');
const path = require('path');
const port  = 3000;

const getType = (url) => {
  const extname = path.extname(url);
  const types = {
    ".html": "text/html",
    ".css": "text/css",
    ".js": "text/javascript",
    ".png": "image/png",
    ".gif": "image/gif",
    ".svg": "svg+xml"
  };
  return ( extname in types )? types[extname] : "text/plain";
};

const getErrorStatusCode = (err) => {
  let status = 500;
  switch(err.code) {
    case 'ENOENT':
    default:
      status = 404;
    break;
  }
  return status;
};

const server = http.createServer((req, res) => {
  const url = path.relative('/', req.url);

  fs.readFile(url, (err, data) => {
    if(!err) {
      res.writeHead(200, {'Content-Type': getType(url)});
      res.end(data);
    } else {
      // throw err するとコケる
      console.log(err, err.code);
      const statusCode = getErrorStatusCode(err);
      res.statusCode = statusCode
      res.writeHead(statusCode, {'Content-Type': 'text/plain'});
      res.end(err.message);
    }
  });
}).listen(port);

console.log(`Server running at http://localhost:${port}/`);

起動

$ node server.js

localhost:3000にアクセス
ルーティングとか特に何もしてないので、server.jsがあるディレクトリがルートなシンプルなサーバです。

作ってみて自分で言うのも何ですが、単に静的ファイル表示できるだけなら、http-server使った方が楽そうです。
それこそちょっと複雑なコトしたければExpressやVue

おまけ

先日参加したReproさんのハンズオンで、json-serverってシンプルなJSON返すサーバーがあることを教えてもらいました。nodeで動かせるので、nodeでサーバーも動かしてAPI使ったアプリのモックアップづくりにもとても良さそうだなーって思いました。


HARIO (ハリオ) V60 コーヒーサーバー 700ml VCS-02B

HARIO (ハリオ) V60 コーヒーサーバー 700ml VCS-02B