Chakraエンジン対応のWSHスクリプトをダブルクリックで実行できるようにする

#概要

WSHで使われているJScriptは大変古い仕様のJavaScriptですが、コマンドラインから//E:でCLSID等を指定すると任意のスクリプトエンジンを設定でき、
Windows10ではEdge内蔵のChakraエンジンを指定することで新しい仕様のJavascriptがWSHで使えるようになっています。

[WSH JScript Chakra を使用した ES2015(ES6) 対応 （ スクリプトエンジン まとめ ）](https://inemaru.hatenablog.com/entry/2018/04/05/055557)

しかし、WSHのキモであるダブルクリックでの実行では自動的に古いJScriptエンジンで実行してしまうので、
Chakraエンジンでの実行をバッチファイル埋め込みではなく.jsファイルをダブルクリックするだけ実行できるようにしてみました。

#スクリプト(Ver.1)
古いJScriptエンジンで起動されたらChakraエンジンを指定して起動しなおすという単純な仕組みになっています。

``` chakraV1.js
/*@cc_on
@if (1)
//この部分は古いJScriptエンジンで実行されます。
var sh = WScript.CreateObject("WScript.Shell");

//Chakraエンジンを指定して同じスクリプトを実行
var command = sh.ExpandEnvironmentStrings("%windir%") + "\\System32\\wscript.exe //E:{1B7CD997-E5FF-4932-A7A6-2A9E636DA385} " + WScript.ScriptFullName;
sh.Exec(command);

//一応終了
WScript.Quit();

@end
@*/
//@if (0)
//↑のコメントは消さないこと！

//Chakraエンジンで動かすJavaScriptをここに記述します。以下は適当なサンプル。
class Test {
  constructor(name) {
    this._name = name;
  }

  output() {
    return 'Hello ' + this._name;
  }
}

class ExTest extends Test {
  output() {
    return  super.output() + '!!';
  }
}

var test = new Test('World');
WScript.Echo(test.output()); //Hello World

var extest = new ExTest('Worlds');
var sh = WScript.CreateObject("WScript.Shell");
sh.Popup(extest.output()); //Hello Worlds!!

//↓のコメントは消さないこと！
//@end
```

#スクリプト(Ver.2)
WSHはUTF-8に対応していないので、Ver.1でハードコーディングした日本語文字列を表示しようとすると文字化けします。
直接メモ帳等のエディタやIDEでコーディングするならShift-JIS(ANSI)で保存すればVer.1でも対応できますが、
WSHはJScriptの範囲ではどうあがいてもUTF-8の文字列は正しく扱わないし、TypeScriptはどうもUTF-8固定でトランスパイルする模様(えぇ…)なので、
Chakraエンジンで再起動する前にUTF-8からShift-JISに変換した一時ファイルを出力することで対応してみました。

よくよく考えればどうせTypeScript使うならタスクランナーからエンコード変換するプラグインを使ってShift-JISに変換したほうがいいような気がしてます。

``` chakraV2.js
/*@cc_on
@if (1)
//この部分は古いJScriptエンジンで実行されます。
var sh = WScript.CreateObject("WScript.Shell");
var fso = WScript.CreateObject("Scripting.FileSystemObject");
var ads = WScript.CreateObject("ADODB.Stream");

//このスクリプトをUTF-8指定で読み込む
ads.charset =  "UTF-8";
ads.Open();
ads.LoadFromFile(WScript.ScriptFullName);
var text = ads.ReadText(-1);
ads.Close();

//一時ファイルとして_付きのファイル名を設定
var outf = fso.GetFile(WScript.ScriptFullName).ParentFolder.Path + "\\_" + WScript.ScriptName;

//Shift-JIS指定で一時ファイルに書き込み
ads.charset = "Shift-JIS";
ads.Open();
ads.WriteText(text, 1);
ads.SaveToFile(outf, 2);
ads.Close();

//Chakraエンジンを指定して一時ファイルとして書き込んだスクリプトを実行、終了まで待つ。
var command = sh.ExpandEnvironmentStrings("%windir%") + "\\System32\\wscript.exe //E:{1B7CD997-E5FF-4932-A7A6-2A9E636DA385} " + outf;
sh.run(command, 1, true);

//一時ファイルを削除
fso.DeleteFile(outf);

//一応終了
WScript.Quit();

@end
@*/
//@if (0)
//↑のコメントは消さないこと！

//Chakraエンジンで動かすJavaScriptをここに記述します。以下は適当なサンプル。
class Test {
    constructor(name) {
        this.name = name;
    }
    output() {
        return 'Hello ' + this.name;
    }
}

class ExTest extends Test {
    output() {
        return super.output() + '!!';
    }
}

var test = new Test('わーるど');
WScript.Echo(test.output()); //Hello わーるど

var extest = new ExTest('わーるず');
var sh = WScript.CreateObject("WScript.Shell");
sh.Popup(extest.output()); //Hello わーるず!!

//↓のコメントは消さないこと！
//@end
```


#メリット
JavaScriptとして直接記述するならバッチファイルに埋め込みでもいいと思いますが、
TypeScript等のaltjsでトランスパイルした場合にファイル名の変更などの処理しなくても出力されたjavascriptファイルでそのままダブルクリック起動ができるようになるのがメリットだと思います。
TypeScriptでWSH(Chakraエンジン使用)スクリプトを作成する方法~~も記事にしたいと思っています。~~
→記事書きました。[TypeScriptでWSH(Chakra)を書く(2020年版)](https://qiita.com/riafeed/items/1e483d8855e365c3d3e6)

#やっぱりバッチファイルに埋め込むほうがよさそう

こちら「[Windowsでshebangもどき、またはバッチにスクリプトを埋め込む方法](https://qiita.com/snipsnipsnip/items/50e4ca88e3ce3f8cffda)」をどうぞ。

#仕組み

Chakraエンジンは従来のJScriptに存在していた条件付きコンパイルに対応していないこと、
JScriptの条件付きコンパイルではステートメントとコードをコメント内に埋め込める機能があることを利用しています。
これで条件付きコンパイルに対応していないChakraエンジンはコメント内の条件付きコンパイルをただのコメントとして扱い、
古いJScriptはコメント内の条件付きコンパイルを解釈して適用するコードを判断するので、
これによって起動したスクリプトエンジンの判別が可能になっています。
コンパイルエラーにならないようコメント付きの//@if (0)～//@endで囲むことでChakraが実行すべきコメント外のコードを古いJScriptエンジンでは実行対象外のコードになるようにしています。

#参考

・[WSH JScript Chakra を使用した ES2015(ES6) 対応 （ スクリプトエンジン まとめ ）](https://inemaru.hatenablog.com/entry/2018/04/05/055557)
・[WSH JScriptを使いこなそう](https://jscript.zouri.jp/)
・[JavaScriptの条件付きコンパイル](http://chalcedony.hatenablog.com/entry/20090423/1240481596)