2021年6月21日月曜日

denoが作成するcacheファイルを使ってTypeScriptをJavaScriptに変換する

denoはTypeScriptをJavaScriptに変換してから実行します。
その変換後のキャッシュファイルが必要になったので、抽出するプログラムを作成しました。



変換後のファイルが必要になった経緯


denoで動作するサーバプログラムはTypeScriptで書きたいです。
ブラウザ上で動作するアプリもTypeScriptで書きたいところですが、まだサポートされていません。
しかし、できればトランスパイラは使いたくないです。(denoもTSからJSに変換してからコンパイルしていますが、それはそういう仕様だからOK)
仕方がないのでブラウザ用のプログラムはJavaScriptで書きます。

denoでもブラウザでも必要になるような汎用的な関数がありました。
同じ関数をTypeScriptとJavaScriptの両方で書くのは避けたいです。
JavaScriptで書いて、型定義ファイルを作成してTypeScriptと混在させる選択肢も検討して色々試してみました。
しかし以下の問題がありました。
  • 型定義ファイルの書き方が分からない。
  • そもそもJavaScriptを使うために型定義ファイルを書く、というのはTypeScriptとJavaScriptの両方で同じ関数を書くのと変わらない気がする。

型定義ファイルについて試行錯誤している最中に「deno run」する際キャッシュファイル(JSファイル)が作成されているのを思い出しました。

手書きで型定義ファイルを作るよりdenoで作成されたJSファイルを利用する方が不一致リスクが低減できると思われます。

以上の理由により、以下のプログラムを作成しました。



変換プログラム


// --allow-run --allow-read --allow-write
import * as path from "https://deno.land/std@0.97.0/path/mod.ts"
function 絶対Pathに変換(p: string): string{
return path.fromFileUrl(new URL(p, import.meta.url))
}
async function cmd(cmd:string): Promise<string>{
const p = Deno.run({cmd:['cmd', '/C', 'chcp 65001 & '+cmd], stdout:'piped'})
const o = await p.output()
const text = new TextDecoder().decode(o)
return text
}
const txt = await cmd('deno info')
const pathGen = txt.replace(/\\\\/g,'\\').match(/Emitted[^:]+:[^"]+"([^\n]+)"/)![1] + '\\'
console.log('Deno.args.length['+Deno.args.length+']')
for(let i=0, L=Deno.args.length; i<L ;i++){
const pathTS = 絶対Pathに変換(Deno.args[i])
const text = await cmd('deno cache '+pathTS)
console.log(text)
const pathUNC = pathTS.indexOf('\\\\')==0 ? 'UNC' : 'file'
const pathJS = pathGen + pathUNC + '\\' + pathTS.replace(/\\+/,'\\').replace(/:/,'') + '.js'
console.log('コピー元['+pathJS+']')
console.log('コピー先['+pathTS+'.js]')
await Deno.copyFile(pathJS, pathTS+'.js')
}
view raw ts2js.ts hosted with ❤ by GitHub



使用例


deno run --allow-run --allow-read --allow-write ts2js.ts common.ts
view raw run.cmd hosted with ❤ by GitHub
上の例では末尾のファイル名「common.ts」がJavascriptへの変換対象になっています。



実行結果





変換対象のファイルPathに「.js」を追加したPathに、変換後のファイルが抽出されます。
// 変換前
function for0L(arr:any, fun:any){
for(let i=0, L=arr.length; i<L ;i++){ const v = fun(i, arr[i])
if(v){return v}
}
}
function forIn(obj:any, fun:any){
for(const name in obj){
const v = fun(name, obj[name])
if(v){return v}
}
}
function get日時(dt:Date = new Date()) {
const f = (num:number, 桁数:number) => { return ('000' + num).slice(-桁数) }
const obj = {
FullYear : 4,
Month : 2,
Date : 2,
Hours : 2,
Minutes : 2,
Seconds : 2,
Milliseconds : 3
}
const 間 = '// ::: '.split('')
let str出力 = ''
forIn(obj, (name:string, 桁数:number)=>{
str出力 += f(eval('dt.get'+name+'()') + (name=='Month'?1:0), 桁数) + 間.shift()
})
return str出力.slice(0, str出力.length-1)
}
view raw common.ts hosted with ❤ by GitHub
// 変換後
function for0L(arr, fun) {
for (let i = 0, L = arr.length; i < L; i++) {
const v = fun(i, arr[i]);
if (v) {
return v;
}
}
}
function forIn(obj, fun) {
for (const name in obj) {
const v = fun(name, obj[name]);
if (v) {
return v;
}
}
}
function get日時(dt = new Date()) {
const f = (num, 桁数) => { return ('000' + num).slice(-桁数); };
const obj = {
FullYear: 4,
Month: 2,
Date: 2,
Hours: 2,
Minutes: 2,
Seconds: 2,
Milliseconds: 3
};
const 間 = '// ::: '.split('');
let str出力 = '';
forIn(obj, (name, 桁数) => {
str出力 += f(eval('dt.get' + name + '()') + (name == 'Month' ? 1 : 0), 桁数) + 間.shift();
});
return str出力.slice(0, str出力.length - 1);
}
view raw common.ts.js hosted with ❤ by GitHub



抽出されたjsファイルを使用するhtml
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>TS→JS</title>
</head>
<body>
<input id=inp style="width:100%">
<textarea id=ta style="width:100%;height:200px"></textarea>
</body>
<script src="./common.ts.js"></script>
<script>
onload = ()=>{ ta.value = get日時() }
</script>
</html>
view raw index.html hosted with ❤ by GitHub



をedgeで読み込んだ結果



ちなみに「import」を含むtsファイルはjsファイルに変換してもエラーになってしまいます。



0 件のコメント:

コメントを投稿