ES5を手放せたのか
この記事は PLAID Design Advent Calendar 2024 の10日目の記事です。
プレイドのデザインエンジニアのケンジです。
はじめに
Web 開発者やライブラリ開発者にとっては、レガシーなコードよりもモダンなコードを書きたいはずです。ですが、ライブラリやツールの公開によって多くの依存関係を抱え、Web サイトが壊れるリスクを負うこともあります。そのため、ES6+ を古いブラウザでも実行可能な JavaScript である ES5 にトランスパイル[1]しなければならない時代がありました。IE 11 をサポートするためです。ES5 は、Web 開発者にとっては対応必須でした。
ですが、JavaScript ツールの分野では多くの進歩があったため、ブラウザの状況も大きく変化しました。ES5 ブラウザである IE11 が、2022年にサポートを終了して、多くの開発者が ES5 の制約から解放されました。
私は、疑問が浮かびました。本当に ES5 を手放せたのでしょうか。
デフォルト設定はES5
本当に ES5 を手放せたのかいう疑問が浮かんだのは、バンドラーやビルドツールのデフォルト設定を見たときでした。
ソースコードは Babel や tsc などのバンドラーやビルトツールによって、事前に古い構文の JavaScript に変換されます。target
の値を設定することで、どのバージョンの JavaScript に変換するかを設定できます。例えば、古いバージョンのブラウザを使っているときは、古いバージョンのブラウザで実行可能な JavaScript を指定します。
Babel の公式ドキュメントは target
を指定するのを推奨しています。ですが、デフォルト設定は ES5 だったのです。さらに TypeScript(tsc) もデフォルトで ES5 をサポートしています。IE 11のサポートが終了したため、ES6+を指定することが一般的なはずだと私は感じました。
JavaScript に興味を持つ世界中の開発者が回答したアンケートの結果をまとめた State of JavaScript 2023 を見ても分かる通り、Babel や tsc は一般的なツールです。私は、Web上では依然として ES5 へのトランスパイルが一般的なのかもしれないと考えました。私たちは、ES5 を手放せていないのかもしれません。
ES5の課題
そもそも、ES5 のコードを配信することは悪なのでしょうか。もし、ES5 のコードが正常に動作しているなら、「モダン」にするためだけに ES6+ に書き換えることやトランスパイラは必要はないです。しかし、ES5 のコードを配信するとき、最終的にバンドルサイズが著しく増加することがあります。それは、ES6+ で実装して、ES5 にトランスパイルしているときです。多くのポリフィル[2]やトランスパイラによる肥大化するためです。
const inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "goat", type: "meat", quantity: 23 },
];
const result = groupBy(inventory, ({ type }) => type);
const inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "goat", type: "meat", quantity: 23 },
];
var inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "goat", type: "meat", quantity: 23 },
];
var result = inventory.reduce(function(acc, item) {
var groupKey = item.type;
if (!acc[groupKey]) acc[groupKey] = [];
acc[groupKey].push(item);
return acc;
}, {});
var n=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports);var f=n((ar,te)=>{"use strict";var P=function(r){return r&&r.Math===Math&&r};te.exports=P(typeof globalThis=="object"&&globalThis)||P(typeof window=="object"&&window)||P(typeof self=="object"&&self)||P(typeof global=="object"&&global)||P(typeof ar=="object"&&ar)||function(){return this}()||Function("return this")()});var d=n((Is,ie)=>{"use strict";ie.exports=function(r){try{return!!r()}catch{return!0}}});var h=n((xs,ne)=>{"use strict";var nn=d();ne.exports=!nn(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!==7})});var D=n((Ps,ae)=>{"use strict";var an=d();ae.exports=!an(function(){var r=function(){}.bind();return typeof r!="function"||r.hasOwnProperty("prototype")})});var w=n((js,ue)=>{"use strict";var un=D(),B=Function.prototype.call;ue.exports=un?B.bind(B):function(){return B.apply(B,arguments)}});var ve=n(ce=>{"use strict";var oe={}.propertyIsEnumerable,se=Object.getOwnPropertyDescriptor,on=se&&!oe.call({1:2},1);ce.f=on?function(e){var t=se(this,e);return!!t&&t.enumerable}:oe});var ur=n((_s,le)=>{"use strict";le.exports=function(r,e){return{enumerable:!(r&1),configurable:!(r&2),writable:!(r&4),value:e}}});var p=n((Cs,ye)=>{"use strict";var fe=D(),pe=Function.prototype,or=pe.call,sn=fe&&pe.bind.bind(or,or);ye.exports=fe?sn:function(r){return function(){return or.apply(r,arguments)}}});var A=n((Ns,be)=>{"use strict";var qe=p(),cn=qe({}.toString),vn=qe("".slice);be.exports=function(r){return vn(cn(r),8,-1)}});var he=n((Fs,de)=>{"use strict";var ln=p(),fn=d(),pn=A(),sr=Object,yn=ln("".split);de.exports=fn(function(){return!sr("z").propertyIsEnumerable(0)})?function(r){return pn(r)==="String"?yn(r,""):sr(r)}:sr});var K=n((Ms,ge)=>{"use strict";ge.exports=function(r){return r==null}});var L=n((Ds,Oe)=>{"use strict";var qn=K(),bn=TypeError;Oe.exports=function(r){if(qn(r))throw new bn("Can't call method on "+r);return r}});var j=n((Bs,Se)=>{"use strict";var dn=he(),hn=L();Se.exports=function(r){return dn(hn(r))}});var y=n((As,Te)=>{"use strict";var cr=typeof document=="object"&&document.all;Te.exports=typeof cr>"u"&&cr!==void 0?function(r){return typeof r=="function"||r===cr}:function(r){return typeof r=="function"}});var m=n((Ks,we)=>{"use strict";var gn=y();we.exports=function(r){return typeof r=="object"?r!==null:gn(r)}});var R=n((Ls,Ee)=>{"use strict";var vr=f(),On=y(),Sn=function(r){return On(r)?r:void 0};Ee.exports=function(r,e){return arguments.length<2?Sn(vr[r]):vr[r]&&vr[r][e]}});var lr=n((Gs,me)=>{"use strict";var Tn=p();me.exports=Tn({}.isPrototypeOf)});var je=n(($s,Pe)=>{"use strict";var wn=f(),Ie=wn.navigator,xe=Ie&&Ie.userAgent;Pe.exports=xe?String(xe):""});var De=n((Us,Me)=>{"use strict";var Fe=f(),fr=je(),Re=Fe.process,_e=Fe.Deno,Ce=Re&&Re.versions||_e&&_e.version,Ne=Ce&&Ce.v8,q,G;Ne&&(q=Ne.split("."),G=q[0]>0&&q[0]<4?1:+(q[0]+q[1]));!G&&fr&&(q=fr.match(/Edge\/(\d+)/),(!q||q[1]>=74)&&(q=fr.match(/Chrome\/(\d+)/),q&&(G=+q[1])));Me.exports=G});var pr=n((Vs,Ae)=>{"use strict";var Be=De(),En=d(),mn=f(),In=mn.String;Ae.exports=!!Object.getOwnPropertySymbols&&!En(function(){var r=Symbol("symbol detection");return!In(r)||!(Object(r)instanceof Symbol)||!Symbol.sham&&Be&&Be<41})});var yr=n((Ws,Ke)=>{"use strict";var xn=pr();Ke.exports=xn&&!Symbol.sham&&typeof Symbol.iterator=="symbol"});var qr=n((ks,Le)=>{"use strict";var Pn=R(),jn=y(),Rn=lr(),_n=yr(),Cn=Object;Le.exports=_n?function(r){return typeof r=="symbol"}:function(r){var e=Pn("Symbol");return jn(e)&&Rn(e.prototype,Cn(r))}});var $=n((zs,Ge)=>{"use strict";var Nn=String;Ge.exports=function(r){try{return Nn(r)}catch{return"Object"}}});var _=n((Ys,$e)=>{"use strict";var Fn=y(),Mn=$(),Dn=TypeError;$e.exports=function(r){if(Fn(r))return r;throw new Dn(Mn(r)+" is not a function")}});var U=n((Hs,Ue)=>{"use strict";var Bn=_(),An=K();Ue.exports=function(r,e){var t=r[e];return An(t)?void 0:Bn(t)}});var We=n((Xs,Ve)=>{"use strict";var br=w(),dr=y(),hr=m(),Kn=TypeError;Ve.exports=function(r,e){var t,i;if(e==="string"&&dr(t=r.toString)&&!hr(i=br(t,r))||dr(t=r.valueOf)&&!hr(i=br(t,r))||e!=="string"&&dr(t=r.toString)&&!hr(i=br(t,r)))return i;throw new Kn("Can't convert object to primitive value")}});var ze=n((Js,ke)=>{"use strict";ke.exports=!1});var V=n((Zs,He)=>{"use strict";var Ye=f(),Ln=Object.defineProperty;He.exports=function(r,e){try{Ln(Ye,r,{value:e,configurable:!0,writable:!0})}catch{Ye[r]=e}return e}});var W=n((Qs,Ze)=>{"use strict";var Gn=ze(),$n=f(),Un=V(),Xe="__core-js_shared__",Je=Ze.exports=$n[Xe]||Un(Xe,{});(Je.versions||(Je.versions=[])).push({version:"3.39.0",mode:Gn?"pure":"global",copyright:"\xA9 2014-2024 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.39.0/LICENSE",source:"https://github.com/zloirock/core-js"})});var gr=n((rc,rt)=>{"use strict";var Qe=W();rt.exports=function(r,e){return Qe[r]||(Qe[r]=e||{})}});var tt=n((ec,et)=>{"use strict";var Vn=L(),Wn=Object;et.exports=function(r){return Wn(Vn(r))}});var S=n((tc,it)=>{"use strict";var kn=p(),zn=tt(),Yn=kn({}.hasOwnProperty);it.exports=Object.hasOwn||function(e,t){return Yn(zn(e),t)}});var Or=n((ic,nt)=>{"use strict";var Hn=p(),Xn=0,Jn=Math.random(),Zn=Hn(1 .toString);nt.exports=function(r){return"Symbol("+(r===void 0?"":r)+")_"+Zn(++Xn+Jn,36)}});var x=n((nc,ut)=>{"use strict";var Qn=f(),ra=gr(),at=S(),ea=Or(),ta=pr(),ia=yr(),I=Qn.Symbol,Sr=ra("wks"),na=ia?I.for||I:I&&I.withoutSetter||ea;ut.exports=function(r){return at(Sr,r)||(Sr[r]=ta&&at(I,r)?I[r]:na("Symbol."+r)),Sr[r]}});var vt=n((ac,ct)=>{"use strict";var aa=w(),ot=m(),st=qr(),ua=U(),oa=We(),sa=x(),ca=TypeError,va=sa("toPrimitive");ct.exports=function(r,e){if(!ot(r)||st(r))return r;var t=ua(r,va),i;if(t){if(e===void 0&&(e="default"),i=aa(t,r,e),!ot(i)||st(i))return i;throw new ca("Can't convert object to primitive value")}return e===void 0&&(e="number"),oa(r,e)}});var k=n((uc,lt)=>{"use strict";var la=vt(),fa=qr();lt.exports=function(r){var e=la(r,"string");return fa(e)?e:e+""}});var wr=n((oc,pt)=>{"use strict";var pa=f(),ft=m(),Tr=pa.document,ya=ft(Tr)&&ft(Tr.createElement);pt.exports=function(r){return ya?Tr.createElement(r):{}}});var Er=n((sc,yt)=>{"use strict";var qa=h(),ba=d(),da=wr();yt.exports=!qa&&!ba(function(){return Object.defineProperty(da("div"),"a",{get:function(){return 7}}).a!==7})});var mr=n(bt=>{"use strict";var ha=h(),ga=w(),Oa=ve(),Sa=ur(),Ta=j(),wa=k(),Ea=S(),ma=Er(),qt=Object.getOwnPropertyDescriptor;bt.f=ha?qt:function(e,t){if(e=Ta(e),t=wa(t),ma)try{return qt(e,t)}catch{}if(Ea(e,t))return Sa(!ga(Oa.f,e,t),e[t])}});var Ir=n((vc,dt)=>{"use strict";var Ia=h(),xa=d();dt.exports=Ia&&xa(function(){return Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype!==42})});var T=n((lc,ht)=>{"use strict";var Pa=m(),ja=String,Ra=TypeError;ht.exports=function(r){if(Pa(r))return r;throw new Ra(ja(r)+" is not an object")}});var C=n(Ot=>{"use strict";var _a=h(),Ca=Er(),Na=Ir(),z=T(),gt=k(),Fa=TypeError,xr=Object.defineProperty,Ma=Object.getOwnPropertyDescriptor,Pr="enumerable",jr="configurable",Rr="writable";Ot.f=_a?Na?function(e,t,i){if(z(e),t=gt(t),z(i),typeof e=="function"&&t==="prototype"&&"value"in i&&Rr in i&&!i[Rr]){var a=Ma(e,t);a&&a[Rr]&&(e[t]=i.value,i={configurable:jr in i?i[jr]:a[jr],enumerable:Pr in i?i[Pr]:a[Pr],writable:!1})}return xr(e,t,i)}:xr:function(e,t,i){if(z(e),t=gt(t),z(i),Ca)try{return xr(e,t,i)}catch{}if("get"in i||"set"in i)throw new Fa("Accessors not supported");return"value"in i&&(e[t]=i.value),e}});var _r=n((pc,St)=>{"use strict";var Da=h(),Ba=C(),Aa=ur();St.exports=Da?function(r,e,t){return Ba.f(r,e,Aa(1,t))}:function(r,e,t){return r[e]=t,r}});var Et=n((yc,wt)=>{"use strict";var Cr=h(),Ka=S(),Tt=Function.prototype,La=Cr&&Object.getOwnPropertyDescriptor,Nr=Ka(Tt,"name"),Ga=Nr&&function(){}.name==="something",$a=Nr&&(!Cr||Cr&&La(Tt,"name").configurable);wt.exports={EXISTS:Nr,PROPER:Ga,CONFIGURABLE:$a}});var It=n((qc,mt)=>{"use strict";var Ua=p(),Va=y(),Fr=W(),Wa=Ua(Function.toString);Va(Fr.inspectSource)||(Fr.inspectSource=function(r){return Wa(r)});mt.exports=Fr.inspectSource});var jt=n((bc,Pt)=>{"use strict";var ka=f(),za=y(),xt=ka.WeakMap;Pt.exports=za(xt)&&/native code/.test(String(xt))});var Mr=n((dc,_t)=>{"use strict";var Ya=gr(),Ha=Or(),Rt=Ya("keys");_t.exports=function(r){return Rt[r]||(Rt[r]=Ha(r))}});var Y=n((hc,Ct)=>{"use strict";Ct.exports={}});var Dt=n((gc,Mt)=>{"use strict";var Xa=jt(),Ft=f(),Ja=m(),Za=_r(),Dr=S(),Br=W(),Qa=Mr(),ru=Y(),Nt="Object already initialized",Ar=Ft.TypeError,eu=Ft.WeakMap,H,N,X,tu=function(r){return X(r)?N(r):H(r,{})},iu=function(r){return function(e){var t;if(!Ja(e)||(t=N(e)).type!==r)throw new Ar("Incompatible receiver, "+r+" required");return t}};Xa||Br.state?(b=Br.state||(Br.state=new eu),b.get=b.get,b.has=b.has,b.set=b.set,H=function(r,e){if(b.has(r))throw new Ar(Nt);return e.facade=r,b.set(r,e),e},N=function(r){return b.get(r)||{}},X=function(r){return b.has(r)}):(E=Qa("state"),ru[E]=!0,H=function(r,e){if(Dr(r,E))throw new Ar(Nt);return e.facade=r,Za(r,E,e),e},N=function(r){return Dr(r,E)?r[E]:{}},X=function(r){return Dr(r,E)});var b,E;Mt.exports={set:H,get:N,has:X,enforce:tu,getterFor:iu}});var Lt=n((Oc,Kt)=>{"use strict";var Lr=p(),nu=d(),au=y(),J=S(),Kr=h(),uu=Et().CONFIGURABLE,ou=It(),At=Dt(),su=At.enforce,cu=At.get,Bt=String,Z=Object.defineProperty,vu=Lr("".slice),lu=Lr("".replace),fu=Lr([].join),pu=Kr&&!nu(function(){return Z(function(){},"length",{value:8}).length!==8}),yu=String(String).split("String"),qu=Kt.exports=function(r,e,t){vu(Bt(e),0,7)==="Symbol("&&(e="["+lu(Bt(e),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),t&&t.getter&&(e="get "+e),t&&t.setter&&(e="set "+e),(!J(r,"name")||uu&&r.name!==e)&&(Kr?Z(r,"name",{value:e,configurable:!0}):r.name=e),pu&&t&&J(t,"arity")&&r.length!==t.arity&&Z(r,"length",{value:t.arity});try{t&&J(t,"constructor")&&t.constructor?Kr&&Z(r,"prototype",{writable:!1}):r.prototype&&(r.prototype=void 0)}catch{}var i=su(r);return J(i,"source")||(i.source=fu(yu,typeof e=="string"?e:"")),r};Function.prototype.toString=qu(function(){return au(this)&&cu(this).source||ou(this)},"toString")});var $t=n((Sc,Gt)=>{"use strict";var bu=y(),du=C(),hu=Lt(),gu=V();Gt.exports=function(r,e,t,i){i||(i={});var a=i.enumerable,u=i.name!==void 0?i.name:e;if(bu(t)&&hu(t,u,i),i.global)a?r[e]=t:gu(e,t);else{try{i.unsafe?r[e]&&(a=!0):delete r[e]}catch{}a?r[e]=t:du.f(r,e,{value:t,enumerable:!1,configurable:!i.nonConfigurable,writable:!i.nonWritable})}return r}});var Vt=n((Tc,Ut)=>{"use strict";var Ou=Math.ceil,Su=Math.floor;Ut.exports=Math.trunc||function(e){var t=+e;return(t>0?Su:Ou)(t)}});var Gr=n((wc,Wt)=>{"use strict";var Tu=Vt();Wt.exports=function(r){var e=+r;return e!==e||e===0?0:Tu(e)}});var zt=n((Ec,kt)=>{"use strict";var wu=Gr(),Eu=Math.max,mu=Math.min;kt.exports=function(r,e){var t=wu(r);return t<0?Eu(t+e,0):mu(t,e)}});var Ht=n((mc,Yt)=>{"use strict";var Iu=Gr(),xu=Math.min;Yt.exports=function(r){var e=Iu(r);return e>0?xu(e,9007199254740991):0}});var $r=n((Ic,Xt)=>{"use strict";var Pu=Ht();Xt.exports=function(r){return Pu(r.length)}});var Qt=n((xc,Zt)=>{"use strict";var ju=j(),Ru=zt(),_u=$r(),Jt=function(r){return function(e,t,i){var a=ju(e),u=_u(a);if(u===0)return!r&&-1;var o=Ru(i,u),s;if(r&&t!==t){for(;u>o;)if(s=a[o++],s!==s)return!0}else for(;u>o;o++)if((r||o in a)&&a[o]===t)return r||o||0;return!r&&-1}};Zt.exports={includes:Jt(!0),indexOf:Jt(!1)}});var Vr=n((Pc,ei)=>{"use strict";var Cu=p(),Ur=S(),Nu=j(),Fu=Qt().indexOf,Mu=Y(),ri=Cu([].push);ei.exports=function(r,e){var t=Nu(r),i=0,a=[],u;for(u in t)!Ur(Mu,u)&&Ur(t,u)&&ri(a,u);for(;e.length>i;)Ur(t,u=e[i++])&&(~Fu(a,u)||ri(a,u));return a}});var Q=n((jc,ti)=>{"use strict";ti.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]});var ni=n(ii=>{"use strict";var Du=Vr(),Bu=Q(),Au=Bu.concat("length","prototype");ii.f=Object.getOwnPropertyNames||function(e){return Du(e,Au)}});var ui=n(ai=>{"use strict";ai.f=Object.getOwnPropertySymbols});var si=n((Cc,oi)=>{"use strict";var Ku=R(),Lu=p(),Gu=ni(),$u=ui(),Uu=T(),Vu=Lu([].concat);oi.exports=Ku("Reflect","ownKeys")||function(e){var t=Gu.f(Uu(e)),i=$u.f;return i?Vu(t,i(e)):t}});var li=n((Nc,vi)=>{"use strict";var ci=S(),Wu=si(),ku=mr(),zu=C();vi.exports=function(r,e,t){for(var i=Wu(e),a=zu.f,u=ku.f,o=0;o<i.length;o++){var s=i[o];!ci(r,s)&&!(t&&ci(t,s))&&a(r,s,u(e,s))}}});var pi=n((Fc,fi)=>{"use strict";var Yu=d(),Hu=y(),Xu=/#|\.prototype\./,F=function(r,e){var t=Zu[Ju(r)];return t===ro?!0:t===Qu?!1:Hu(e)?Yu(e):!!e},Ju=F.normalize=function(r){return String(r).replace(Xu,".").toLowerCase()},Zu=F.data={},Qu=F.NATIVE="N",ro=F.POLYFILL="P";fi.exports=F});var Wr=n((Mc,yi)=>{"use strict";var rr=f(),eo=mr().f,to=_r(),io=$t(),no=V(),ao=li(),uo=pi();yi.exports=function(r,e){var t=r.target,i=r.global,a=r.stat,u,o,s,v,c,O;if(i?o=rr:a?o=rr[t]||no(t,{}):o=rr[t]&&rr[t].prototype,o)for(s in e){if(c=e[s],r.dontCallGetSet?(O=eo(o,s),v=O&&O.value):v=o[s],u=uo(i?s:t+(a?".":"#")+s,r.forced),!u&&v!==void 0){if(typeof c==typeof v)continue;ao(c,v)}(r.sham||v&&v.sham)&&to(c,"sham",!0),io(o,s,c,r)}}});var bi=n((Dc,qi)=>{"use strict";var oo=Vr(),so=Q();qi.exports=Object.keys||function(e){return oo(e,so)}});var hi=n(di=>{"use strict";var co=h(),vo=Ir(),lo=C(),fo=T(),po=j(),yo=bi();di.f=co&&!vo?Object.defineProperties:function(e,t){fo(e);for(var i=po(t),a=yo(t),u=a.length,o=0,s;u>o;)lo.f(e,s=a[o++],i[s]);return e}});var Oi=n((Ac,gi)=>{"use strict";var qo=R();gi.exports=qo("document","documentElement")});var Pi=n((Kc,xi)=>{"use strict";var bo=T(),ho=hi(),Si=Q(),go=Y(),Oo=Oi(),So=wr(),To=Mr(),Ti=">",wi="<",zr="prototype",Yr="script",mi=To("IE_PROTO"),kr=function(){},Ii=function(r){return wi+Yr+Ti+r+wi+"/"+Yr+Ti},Ei=function(r){r.write(Ii("")),r.close();var e=r.parentWindow.Object;return r=null,e},wo=function(){var r=So("iframe"),e="java"+Yr+":",t;return r.style.display="none",Oo.appendChild(r),r.src=String(e),t=r.contentWindow.document,t.open(),t.write(Ii("document.F=Object")),t.close(),t.F},er,tr=function(){try{er=new ActiveXObject("htmlfile")}catch{}tr=typeof document<"u"?document.domain&&er?Ei(er):wo():Ei(er);for(var r=Si.length;r--;)delete tr[zr][Si[r]];return tr()};go[mi]=!0;xi.exports=Object.create||function(e,t){var i;return e!==null?(kr[zr]=bo(e),i=new kr,kr[zr]=null,i[mi]=e):i=tr(),t===void 0?i:ho.f(i,t)}});var ji=n(()=>{"use strict";var Eo=Wr(),mo=h(),Io=Pi();Eo({target:"Object",stat:!0,sham:!mo},{create:Io})});var _i=n(($c,Ri)=>{"use strict";var xo=A(),Po=p();Ri.exports=function(r){if(xo(r)==="Function")return Po(r)}});var Fi=n((Uc,Ni)=>{"use strict";var Ci=_i(),jo=_(),Ro=D(),_o=Ci(Ci.bind);Ni.exports=function(r,e){return jo(r),e===void 0?r:Ro?_o(r,e):function(){return r.apply(e,arguments)}}});var Hr=n((Vc,Mi)=>{"use strict";Mi.exports={}});var Bi=n((Wc,Di)=>{"use strict";var Co=x(),No=Hr(),Fo=Co("iterator"),Mo=Array.prototype;Di.exports=function(r){return r!==void 0&&(No.Array===r||Mo[Fo]===r)}});var Li=n((kc,Ki)=>{"use strict";var Do=x(),Bo=Do("toStringTag"),Ai={};Ai[Bo]="z";Ki.exports=String(Ai)==="[object z]"});var $i=n((zc,Gi)=>{"use strict";var Ao=Li(),Ko=y(),ir=A(),Lo=x(),Go=Lo("toStringTag"),$o=Object,Uo=ir(function(){return arguments}())==="Arguments",Vo=function(r,e){try{return r[e]}catch{}};Gi.exports=Ao?ir:function(r){var e,t,i;return r===void 0?"Undefined":r===null?"Null":typeof(t=Vo(e=$o(r),Go))=="string"?t:Uo?ir(e):(i=ir(e))==="Object"&&Ko(e.callee)?"Arguments":i}});var Xr=n((Yc,Vi)=>{"use strict";var Wo=$i(),Ui=U(),ko=K(),zo=Hr(),Yo=x(),Ho=Yo("iterator");Vi.exports=function(r){if(!ko(r))return Ui(r,Ho)||Ui(r,"@@iterator")||zo[Wo(r)]}});var ki=n((Hc,Wi)=>{"use strict";var Xo=w(),Jo=_(),Zo=T(),Qo=$(),rs=Xr(),es=TypeError;Wi.exports=function(r,e){var t=arguments.length<2?rs(r):e;if(Jo(t))return Zo(Xo(t,r));throw new es(Qo(r)+" is not iterable")}});var Hi=n((Xc,Yi)=>{"use strict";var ts=w(),zi=T(),is=U();Yi.exports=function(r,e,t){var i,a;zi(r);try{if(i=is(r,"return"),!i){if(e==="throw")throw t;return t}i=ts(i,r)}catch(u){a=!0,i=u}if(e==="throw")throw t;if(a)throw i;return zi(i),t}});var rn=n((Jc,Qi)=>{"use strict";var ns=Fi(),as=w(),us=T(),os=$(),ss=Bi(),cs=$r(),Xi=lr(),vs=ki(),ls=Xr(),Ji=Hi(),fs=TypeError,nr=function(r,e){this.stopped=r,this.result=e},Zi=nr.prototype;Qi.exports=function(r,e,t){var i=t&&t.that,a=!!(t&&t.AS_ENTRIES),u=!!(t&&t.IS_RECORD),o=!!(t&&t.IS_ITERATOR),s=!!(t&&t.INTERRUPTED),v=ns(e,i),c,O,M,Jr,g,Zr,Qr,re=function(l){return c&&Ji(c,"normal",l),new nr(!0,l)},ee=function(l){return a?(us(l),s?v(l[0],l[1],re):v(l[0],l[1])):s?v(l,re):v(l)};if(u)c=r.iterator;else if(o)c=r;else{if(O=ls(r),!O)throw new fs(os(r)+" is not iterable");if(ss(O)){for(M=0,Jr=cs(r);Jr>M;M++)if(g=ee(r[M]),g&&Xi(Zi,g))return g;return new nr(!1)}c=vs(r,O)}for(Zr=u?r.next:c.next;!(Qr=as(Zr,c)).done;){try{g=ee(Qr.value)}catch(l){Ji(c,"throw",l)}if(typeof g=="object"&&g&&Xi(Zi,g))return g}return new nr(!1)}});var tn=n(()=>{"use strict";var ps=Wr(),ys=R(),qs=p(),bs=_(),ds=L(),hs=k(),gs=rn(),Os=d(),en=Object.groupBy,Ss=ys("Object","create"),Ts=qs([].push),ws=!en||Os(function(){return en("ab",function(r){return r}).a.length!==1});ps({target:"Object",stat:!0,forced:ws},{groupBy:function(e,t){ds(e),bs(t);var i=Ss(null),a=0;return gs(e,function(u){var o=hs(t(u,a++));o in i?Ts(i[o],u):i[o]=[u]}),i}})});ji();tn();var Es=[{name:"asparagus",type:"vegetables",quantity:5},{name:"bananas",type:"fruit",quantity:0},{name:"goat",type:"meat",quantity:23}],rv=Object.groupBy(Es,function(r){var e=r.type;return e});
ES5にトランスパイルしたコードについて
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": "3.38.1"
}
]
],
"exclude": "**/core-js"
}
const inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "goat", type: "meat", quantity: 23 },
];
const result = groupBy(inventory, ({ type }) => type);
出力されたコードを、bundlejs で検証しています。
検証結果を見ると、ES6+ のコードサイズは 342バイト、ES5 を使用した自前実装だと、コードサイズは 501バイトでした。ですが、ES6+ をES5 にトランスパイラしたら、17,566バイトに肥大化したのです。81個の core-jsポリフィルの依存関係が含まれていました。50倍にもバンドルサイズが肥大化してしまいました。
IE11のためにES5にトランスパイラすることは、バンドルサイズが肥大化することがわかります。問題は単なるコードのバンドルサイズが著しく肥大することだけではありません。ES5 にトランスパイラされたコードが、IE11 でも動きませんでした。ES5 の恩恵を受け取れなかったのです。
現状のES5への状態を理解
デフォルトで ES5 に対応するツールを挙げましたが、当然、ES5 に対応してないツールも存在します。
ツール | 備考 |
---|---|
Browserslist | ビルドツールそのものではありませんが、多くのビルドツールで内部的に使用されており、ブラウザサポートターゲットを設定するための根強いツールです。デフォルト設定にはES5ブラウザは含まれません。最後に含まれていたのはIE 11で、バージョン4.21で非対応。 |
webpack | デフォルトでは webpack はコードをトランスパイルしません。ほとんどの webpack ユーザーは babel-loader を含めており、その使用例では targets: "defaults" を設定することを推奨。 |
Next.js | Next.js は Babel を使用してトランスパイルし、デフォルトで「モダンブラウザ」(esmodulesをサポートするブラウザ)をターゲットとする Browserslist を適用。 |
esbuild | esbuild はデフォルトではトランスパイルしません。トランスパイルを有効にするにはカスタムターゲットを設定できますが、ES5 はターゲットとしてサポートされていません。 |
Vite | esbuildを使用し、デフォルトでモダンブラウザをターゲットに設定しています。レガシーブラウザをサポートする必要があるときは、プラグインをインストールしなければなりません。 |
Rollup | デフォルトではトランスパイルしません。多くの Rollup ユーザーは@rollup/plugin-babel をインストールし、Babel のデフォルト設定を使用しています。 |
Parcel | カスタマイズ可能なターゲットで差分配信を自動的に適用。 |
Closure Compiler | デフォルトではECMASCRIPT_NEXT (最新の安定したES機能セット)を使用。 |
新しいビルドツールに関してはES5 をまったくサポートしていないことがわかります。今後もサポートしないビルドツールは多くなるでしょう。ES5 をサポートするためには、ユーザーが明示的に設定する必要があります。
ほとんどのWeb開発はソースコードをトランスパイルするときに Babel を使用しています。明示的に設定して node_modules
内のコードをトランスパイルしないようにしています。
この慣習が、歴史的にライブラリ開発者が ES5 へのトランスパイルを続ける必要があると感じている理由の一つなのかもしれません。
- Webpack の babel-loader のドキュメントでは、
node_modules
を明示的に除外する設定を推奨。
module: {
rules: [
{
test: /\.(?:js|mjs|cjs)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
targets: "defaults",
presets: [
['@babel/preset-env']
]
}
}
}
]
}
- Rollup の plugin-babel でも
node_modules
を除外する設定を推奨しており、ライブラリ開発者に対して ES6コードを公開しないよう推奨しています。 - TypeScript(tsc)は、プロジェクト自体のコードファイルのみをトランスパイルします。
node_modules
内のプロジェクト依存コードはトランスパイルしません。
バンドラーやビルドツールのデフォルト設定を適切に設定し、ライブラリの構文レベルを確認している Web サイトはどれぐらいあるのでしょうか。ビルドパイプライン全体がどのように相互作用するかを熟知して、それぞれを正しく設定する方法を理解しなければ、ES5 コードと一緒に ES6+ コードをバンドルすることになります。
公開された Web サイトだと、どれぐらい ES5 を手放せているのでしょうか。
公開されているWebサイトを調査
本番環境にデプロイされたE5やES6+がどれくらい含まれているかを調査している記事がありました。 HTTP Archiveで調査しているようです。人気ランキングに基づいたトップ10,000サイトが対象です。
ES5有無の検索方法について
HTTP Archiveを使用して、最適化されたコード内でも、ES5 を使用しているサイトを検出できます。Web 開発はソースコードをトランスパイルするときに、Babel、TypeScript(tsc)、Closure Compiler を使うはずで、コード出力で重複を避けるために、ポリフィルや ES5 ヘルパー関数を含んでいます。一般的に使用される ES5 ヘルパー関数ライブラリは babel-helpers, core-js, regenerator-runtime, tslib, $jscomp なのですが、多くの ES5 ヘルパーライブラリにある関数は特徴的であるため、標準的な ES5 構文を検出するのではなく、ES5 ヘルパー関数の有無を検索することで、2つに区別しているようです。
- 手作業で書かれた古い ES5 コード
- トランスパイラによって生成された新しい ES5 コード
結果は
- 89%のサイトが、トランスパイルされていない ES6+ を含む JavaScript ファイルを少なくとも1つ配信している。
- 79%のサイトが、ES5 ヘルパーコードを含む JavaScript ファイルを少なくとも1つ配信している。
- 68%のサイトが、ES5 ヘルパーコードとトランスパイルされていないES6+ を同じファイル内に含む JavaScript ファイルを少なくとも1つ配信している。
繰り返しになりますが、ブラウザが ES6+ をサポートしていないとき、ES6+ を含むスクリプトファイルを読み込もうとするとエラーになります。一方で、ブラウザが ES6+ をサポートしているとき、ES5コードやポリフィルは必要ありません。この両方を含める理由は全くありません。
この結果から私が想像していることは、多くの Web サイトが、モダンブラウザ向けに最適化されたコードを配信しているということ。そして、ES5 ポリフィルを含めることで、IE 11 などの古いブラウザでも動作するようにしているということです。
ES5コードとトランスパイラされていないES6+構文を含むコードを提供するサイトについては、次の2つのもっともらしい説明が考えられます。
- IE 11 をサポートする必要がないが、依存関係が ES5 にトランスパイルされているため、結果としてES5コードが出力に含まれている。
- IE 11 をサポートするつもりだったが、依存関係の一部がトランスパイルされていない ES6+ を含むことに気づかず、
node_modules
内のコードをトランスパイルするようバンドラーを設定していなかった。
トランスパイルされていない ES6+ と ES5 の両方を同じバンドルに含めて配信しているという事実は、node_modules
をトランスパイルから除外するという慣習が良いプラクティスではないのかもしれません。node_modules
をトランスパイルすると、ビルドが遅くなるので、除外することが一般的です。ですが、近年はビルドツールは大幅に高速化されてきました。Webサイトは node_modules
内のコードをトランスパイルするのは本番環境のビルド時だけに限定できます。
開発中のコードは、開発者が使用しているブラウザ上で問題なく動作します。
Baselineを活用したアプローチ
私は、IE 11や ES5 をサポートしない方が良いと言いたいわけではありません。IE 11 をサポートするかどうかは、Webサイトのターゲットユーザーに依存します。
ですが、Web開発者やライブラリ開発者は、Baseline に基づいて、どのブラウザをサポートするかを決定しても良いと考えています。
Baselineとは
W3Cの WebDX コミュニティグループが推進する取り組みで、主要なブラウザとブラウザレンダリングエンジンで安定してサポートされている機能を開発者が特定できるようにするものです。ある機能が Baseline Widely Available になるためには、主要な4つのブラウザの安定版で少なくとも30か月間利用できる必要があります。
ライブラリ開発者は、Browserslist クエリを使用して この Baseline Widely Available 機能をターゲットに設定できます。
targets: [
'chrome >0 and last 2.5 years',
'edge >0 and last 2.5 years',
'safari >0 and last 2.5 years',
'firefox >0 and last 2.5 years',
'and_chr >0 and last 2.5 years',
'and_ff >0 and last 2.5 years',
'ios >0 and last 2.5 years',
]
※ Browserslistに Baselineサポートを追加する機能リクエストがオープンになっています。これが実現すれば、上記のクエリは単に baseline widely available と記述するだけで済むようになります。
もし Webサイトが Baseline Widely Available でカバーされる以上のブラウザをサポートする必要があるときはどうすれば良いでしょうか。Web開発者が独自のビルドシステムを設定し、必要なライブラリをさらにトランスパイルすることもできます。Web開発者が実装すべきなのかもしれません。
最後に
読者に理解していただきたい主な点は以下です。
- ビルドツールやライブラリでES5をターゲットとすべきものではない
- ツールやライブラリにとって適切なブラウザサポートポリシーは「Baseline Widely Available」
- ブラウザサポートの決定は、Web開発者によって行われるべき。
- クロスブラウザサポートをビルドツールだけに頼るべきではない。
もし、あなたのサイトが ES5 と ES6+ コードを混在させたコードを配信しているかどうか気になるなら、ぜひ確認してみてください。
参考文献
PLAID Design Advent Calendar 2024 11日目は、 Kenichi Suzuki さんによる、「顧客データを会社の血液にしていきたい話」です。
Discussion