gulpfileをCoffeeScriptで書く

メモ。

//gulpfile.js
require('coffee-script/register');
require('./gulpfile.coffee');
//gulpfile.coffee
gulp = require('gulp')

gulp.task 'default', () ->
  console.log('gulp!')

参考。

普通にCoffeeScriptで書けるようになってた。

node-webkitでコールバックで書いた非同期処理をPromise使用に書き換えてみた

node-webkitで作ったアプリで、データロード処理と保存処理部分をそれぞれ書き換えてみた。
PromiseのライブラリはBluebirdを使ってみた。
あと、cheerioをDOMのパースに使用。
FTPは、jsftpを使用。

データロード処理の流れ(load)
1. FTPでデータ取得
2. FTPで取得したファイルを読み込み
3. 読み込んだHTMLをパースしてデータ取得
4. 入力フォームに取得したデータを反映

保存処理の流れ(save)
1. FTPで取得していたファイルを読み込み
2. 読み込んだHTMLをパースしてフォームに入力されたデータをHTMLに反映
3. フォームのデータが反映されたHTMLのデータを一時ファイルに書き込み
4. 書き込まれた一時ファイルをFTPでアップロード
5. アップロード完了後、別画面に遷移、完了通知などの処理

v1がコールバック版、v2がPromise版

callback版、Promise版

2014/6/25追記:loadとsaveを呼び出している部分(index.coffee)も追加してみた。

File SystemのAPIはBluebirdのPromisificationという機能を使ってみた。

HTML部分の入力フォームやFTPの設定などはgistに上げてませんが、大体やってる流れは把握できるかと。

cheerioでパースすると、日本語が数値文字列参照になっているので、変換。
https://github.com/cheeriojs/cheerio/issues/466

Promiseについては以下が大変参考になりました。

node-webkitでTypeScriptを使ってはまるケース

node-webkitJavaScript Contextでのrequireでのパス解決でハマる場合がある。

例えばこんなファイル構成で

  • index.html
  • js
    • index.js(index.ts)
    • fileUploader.js

各ファイルがこんな感じ。

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head >
    <meta charset="utf-8" />
    .................
    <script src="js/index.js"></script>
  </body>
</html>
//js/index.js
..................
var fileUploader = require('./js/fileUploader');
.................
//↑node-webkit的に問題なし.

//index.ts
import fileUploader = require('./js/fileUploader');
//↑index.tsからだとrequire('./fileUploader')なのでエラー。

↑node-webkitJavaScript Context(いわゆるブラウザの世界)でrequireをした場合、JavaScriptファイルからのパスではなく、そのJavaScriptを読み込んでいるhtmlからのパスになる。

参考:

回避策1

その場しのぎ的。requireをindex.htmlのインラインに持ってくる。

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head >
    <meta charset="utf-8" />
    .................
    <script>
      //..................
       var fileUploader = require('./js/fileUploader');
      //................. 
    </script>
    <script src="
  </body>
</html>

回避策2

構造を変える

  • index.html
  • index.js(index.ts)
  • js
    • fileUploader.js
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head >
    <meta charset="utf-8" />
    .................
    <script src="index.js"></script>
  </body>
</html>

うーむ。

requireしてnewして使用するnpmのパッケージにTypeScript用の型定義ファイルを書く

こういうの。

///<reference path='./typing/node.d.ts' />
///<reference path='./typing/jsftp.d.ts' />

import JSFtp = require('jsftp');

..........................
   var Ftp = new JSFtp({
      host: connect.host,
      port: connect.port,
      user: connect.account,
      pass: connect.password
    });
..........................

jsftp用に書いた型定義ファイル(簡略)

//  ./typing/jsftp.d.ts

/// <reference path="./node.d.ts" />

interface JSFtpOption {
  host?: string;
  port?: number;
  user?: string;
  pass?: string;
  ....................
}

declare module 'jsftp' {

  class Ftp {
    constructor(cfg?: JSFtpOption);
    put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void;
    .......
  }

export = Ftp;
}

参考

declare module 'jsftp' {

  class Ftp {
    constructor(cfg?: {host?:string;port?:number;user?:string;pass?: string});

    put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void;
    ...........
  }

export = Ftp;
}

オブジェクト型リテラルにすれば重複しないけど。 うーむ。

追記

おー、なるほど。

declare module JSFTP {
  export interface JSFtpOption {
    host?: string;
    port?: number;
    user?: string;
    pass?: string;
    .......................
  }
}

declare module 'jsftp' {

  class Ftp {
    constructor(cfg?: JSFTP.JSFtpOption);

    put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void;
    .............................................
  }

export = Ftp;
}

このJSFTPモジュールは非インスタンス化モジュールで、型定義のネームスペースにしか存在しないので、変数空間を汚さないと。
ネームスペースを噛ませているので重複しにくくなりました。
勉強になりました。
ありがとうございます。

更に追記

あー・・・。TypeScriptリファレンス Ver.1.0対応の6-4-4インスタンス化・非インスタンス化モジュールに書いてあった。
読破したはずなのに覚えてなかった・・・。

*1:module.exports=Ftp

node-webkitでNode contextからGUIのAPIを使う

メニュー出したりとかデスクトップアプリ特有のネイティブのアレをNode側から使いたい。

1.require時に引数として渡す

//index.js

var gui = require('nw.gui');
//↓コレ
var fileUploader = require('fileUploader')(gui);

例なので全部渡しちゃってるけど必要な項目だけに絞って渡した方がいいと思う。

2.window.require('nw.gui')する

//fileUploader.js

var fs = require('fs');

var gui = window.require('nw.gui');

Window.globalとかGlobal.windowとかになっている話はこちらを参照

node-webkitの場合、windowにもrequireメソッドがあって、実装はというとこんな

function (name) {  if (name == 'nw.gui')    return nwDispatcher.requireNwGui();  return global.require(name);}

見てそのまま。
nw.guiはwindowのrequireを呼ばないといけないと。

contextが混在しているのはささっと呼べて便利といえば便利だけど、サンドボックス機構があったほうがメンテナンス的には幸せなのかもしれない。
以上。

参考

grunt-slim

メモ
Gruntfile.coffee

  ......................................
    slim:
      pretty:
        options:
          pretty: true
        files: [
          expand: true
          cwd: 'src'
          src: ['{,*/}*.slim']
          dest: 'app'
          ext: '.html'
        ]
  ......................................
  grunt.loadNpmTasks('grunt-slim')

圧縮されるのはアレなのでpretty: trueにした。

参考:grunt-slim