JavaScript: fileのjs化とか

`fetch`で読み込んだfileの文字列をjsとみなして実行。
```js:UTF8形式限定
//非同期
fetch("a.txt").then(a=>a.text().then(a=>eval(a)))
//同期
(async()=>{eval(await(await fetch("a.txt")).text())})()
```
```js:UTF8以外
//TextDecoderの引数に文字符号指定. 以下はshift-jisの例
//非同期
fetch("a.txt").then(a=>a.arrayBuffer().then(a=>eval(new TextDecoder("sjis").decode(a))))
//同期
(async()=>{eval(new TextDecoder("sjis").decode(await(await fetch("a.txt")).arrayBuffer()))})()
```
`eval`などという邪道を使いたくないなら`Function(...)()`を利用したり、以下のようにscript要素を作って中に突っ込んだりするといい
```js:王道
fetch("a.txt").then(a=>a.text().then(txt=>{
var e=document.createElement("script");
	e.type="text/javascript";
	e.charset="utf-8";
	e.text=txt;
	document.getElementsByTagName('head')[0].appendChild(e)
}))
```
ここからが本番。圧縮fileをjsとみなして実行。base64変換等が不要なので手軽な上に圧縮率も落ちない
```js:gzip展開+js実行
//TextDecoderの引数に元の文字符号指定(UTF8なら不要)
fetch("a.js.gz").then(a=>a.arrayBuffer().then(b=>new Response(new Blob([b]).stream().pipeThrough(new DecompressionStream("gzip"))).arrayBuffer().then(c=>eval(new TextDecoder().decode(c)))))
```
### `XMLHttpRequest`を利用した別解
```js:UTF8形式限定
let xhr=new XMLHttpRequest;
xhr.onload=a=>eval(xhr.response);
xhr.responseType="text";
xhr.send(xhr.open("GET","a.txt"))
```
```js:UTF8以外
let xhr=new XMLHttpRequest;
xhr.onload=a=>eval(new TextDecoder("sjis").decode(xhr.response))
xhr.responseType="arraybuffer";
xhr.send(xhr.open("GET", "a.txt"))
```
```js:gzip展開+js実行
let xhr=new XMLHttpRequest;
xhr.onload=a=>new Response(new Blob([xhr.response]).stream().pipeThrough(new DecompressionStream("gzip"))).arrayBuffer().then(b=>eval(new TextDecoder().decode(b)));
xhr.responseType="arraybuffer";
xhr.send(xhr.open("GET","a.js.gz"))
```
ちなみに`send`の引数に`open`の返り値を突っこむ必要はない。普通は以下のように分けて書くものだが、圧縮中毒患者はそんな事しないのだ
```js
xhr.open("GET","a.txt");
xhr.send()
```
## 応用
今時gzipごときの圧縮率では物足りないと言わざるを得ない。巨大fileを圧縮するならLZMAの出番である。どこぞの血迷ったprogrammerが[超小型のdecoder](https://qiita.com/mashuel/items/4e49ccb31c06203f76e1)とやらを公開していらっしゃるので、ありがたく拝借しちゃいましょう(戻り値はUint8Arrayに変更)
```js:lzma.js
// @u: Array/Uint8Array
// @return: Uint8Array
lzma=u=>{for(var a=0,b,c,d=u[0],e=d/9,f=(b,a,d=b[a]||(b[a]=1024),c=d*(h>>>11))=>(c=c>n>>>0?(h=c,b[a]+=2048-d>>5,0):(h-=c,n-=c,b[a]-=d>>5,1),h>>24||(h<<=8,n=n<<8|u[i++]),c),q=(b,a)=>f(b,0)?f(b,1)?18+r(b,8,1):10+r(b,3,a*16+261):2+r(b,3,a*16+511),r=(b,a,c,d=1,e=a)=>{for(;a--;)d+=d+f(b,d+c);return 1<<e^d},s,t,v,w=d%9,j,l,n,m=1<<e/5,g=1<<e%5,p=d=0,A=0,o=[],h=16|g--<<8,i=11,k=i;h--;)f[h]=[];for(;i<18;n=n<<8|u[i++])A=u[k--]+A*256;for(;p<A;d=p&m-1)if(f(f[1],s=a*16+d)){if(f(f[e=k=0],a))f(f[2],a)?(f(f[3],a)?(f(f[4],a)?(e=j,j=t):e=t,t=v):e=v,v=b,b=e):(k=!f(f[5],s))&&(a=7>a?9:11),k||(k=q(f[9],d),a=7>a?8:11);else{k=q(f[8],d),a=7>a?7:10,j=t,t=v,v=b,b=r(f[7],6,6>k?k-2<<6:192);if(b>3){if(s=b>>1,d=14>b,l=31-b+(b=(2|b&1)<<--s),!d){for(l=0;4<s--;d+=d-c)h>>>=1,c=n-h>>>31,n-=h&--c,h>>24||(h<<=8,n=n<<8|u[i++]);b+=d<<++s}for(d=1;s>e;b+=c<<e++)d+=d+=c=f(f[6],d+l);if(b<0)break}}for(e=p+~b;k--;)l=o[p++]=o[e++]}else{l=f[16+(l>>8-w|(p&g)<<w)],d=1;if(7>a)for(;d<256;)d+=d+f(l,d);else{for(e=o[p+~b];d<256;)if(k=e>>7&1,e+=e,d+=d+=c=f(l,d|k+1<<8),c^k)for(;d<256;)d+=d+f(l,d)}l=o[p++]=d-256;a-=4>a?a:9<a?6:3}return new Uint8Array(o)}
```
`TextDecoder()`の引数に元の文字符号を指定するのは前述の通り。以下の例では引数省略(UTF8)
```js:lzma展開+js実行
fetch("a.js.lzma").then(a=>a.arrayBuffer().then(a=>eval(new TextDecoder().decode(lzma(a)))))
```
巨大なwasm fileを圧縮しておいて、展開したものとWebAssemblyを組み合わせるのに役立ちやがるぞ
## 補足
LZMAで圧縮するには[ここ](https://7-zip.opensource.jp/sdk.html)にあるLZMA SDKの書庫に格納されている**lzma.exe**を利用する
```
lzma.exe e a.js a.js.lzma
```
などと実行すれば圧縮fileが生成される。browserで圧縮処理したいなら[hex](https://lo-th.github.io/hex/)を使うといい。ただし圧縮設定は変更できない。[github](https://github.com/lo-th/hex)のexamplesに良い例が丸出しになっている