From cd8698b1d9fd560d85e912acca9a1e24f00e18f8 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Wed, 19 Feb 2020 20:57:14 +0300 Subject: [PATCH] feat: support the `query` template for the `name` option (#366) --- README.md | 57 +++++++++++++++++++- package-lock.json | 14 ++--- package.json | 2 +- test/__snapshots__/name-option.test.js.snap | 10 +++- test/fixtures/cdn.js | 6 +++ test/fixtures/nested/file.png | Bin 0 -> 6777 bytes test/fixtures/simple.js | 2 +- test/name-option.test.js | 37 +++++++++++-- 8 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 test/fixtures/cdn.js create mode 100644 test/fixtures/nested/file.png diff --git a/README.md b/README.md index ca9f27f..3f0187c 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,10 @@ module.exports = { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader', options: { - name(file) { + name(resourcePath, resourceQuery) { + // `resourcePath` - `/absolute/path/to/file.js` + // `resourceQuery` - `?foo=bar` + if (process.env.NODE_ENV === 'development') { return '[path][name].[ext]'; } @@ -439,6 +442,13 @@ Default: `file.folder` The folder of the resource is in. +### `[query]` + +Type: `String` +Default: `file.query` + +The query of the resource, i.e. `?foo=bar`. + ### `[emoji]` Type: `String` @@ -619,6 +629,48 @@ Result: path/to/file.png?e43b20c069c4a01867c31e98cbce33c9 ``` +### CDN + +The following examples show how to use `file-loader` for CDN uses query params. + +**file.js** + +```js +import png from './directory/image.png?width=300&height=300'; +``` + +**webpack.config.js** + +```js +module.exports = { + output: { + publicPath: 'https://cdn.example.com/', + }, + module: { + rules: [ + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: 'file-loader', + options: { + name: '[path][name].[ext][query]', + }, + }, + ], + }, + ], + }, +}; +``` + +Result: + +```bash +# result +https://cdn.example.com/directory/image.png?width=300&height=300 +``` + ### Dynamic public path depending on environment variable at run time An application might want to configure different CDN hosts depending on an environment variable that is only available when running the application. This can be an advantage, as only one build of the application is necessary, which behaves differntly depending on environment variables of the deployment environment. Since file-loader is applied when compiling the application, and not when running it, the environment variable cannot be used in the file-loader configuration. A way around this is setting the `__webpack_public_path__` to the desired CDN host depending on the environment variable at the entrypoint of the application. The option `postTransformPublicPath` can be used to configure a custom path depending on a variable like `__webpack_public_path__`. @@ -626,7 +678,6 @@ An application might want to configure different CDN hosts depending on an envir **main.js** ```js -const namespace = process.env.NAMESPACE; const assetPrefixForNamespace = (namespace) => { switch (namespace) { case 'prod': @@ -641,6 +692,8 @@ const assetPrefixForNamespace = (namespace) => { return ''; } }; +const namespace = process.env.NAMESPACE; + __webpack_public_path__ = `${assetPrefixForNamespace(namespace)}/`; ``` diff --git a/package-lock.json b/package-lock.json index 5bb2aa5..e1098c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4604,9 +4604,9 @@ "dev": true }, "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "end-of-stream": { "version": "1.4.4", @@ -9705,12 +9705,12 @@ "dev": true }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "requires": { "big.js": "^5.2.2", - "emojis-list": "^2.0.0", + "emojis-list": "^3.0.0", "json5": "^1.0.1" } }, diff --git a/package.json b/package.json index 46d9dde..546279c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "webpack": "^4.0.0 || ^5.0.0" }, "dependencies": { - "loader-utils": "^1.2.3", + "loader-utils": "^1.4.0", "schema-utils": "^2.5.0" }, "devDependencies": { diff --git a/test/__snapshots__/name-option.test.js.snap b/test/__snapshots__/name-option.test.js.snap index f9679db..1968f04 100644 --- a/test/__snapshots__/name-option.test.js.snap +++ b/test/__snapshots__/name-option.test.js.snap @@ -1,14 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`"name" option should work for CDN support query params: errors 1`] = `Array []`; + +exports[`"name" option should work for CDN support query params: result 1`] = `"https://cdn.example.com/nested/file.png?foo=bar"`; + +exports[`"name" option should work for CDN support query params: warnings 1`] = `Array []`; + exports[`"name" option should work with "Function" value: errors 1`] = `Array []`; -exports[`"name" option should work with "Function" value: result 1`] = `"9c87cbf3ba33126ffd25ae7f2f6bbafb.function.png"`; +exports[`"name" option should work with "Function" value: result 1`] = `"9c87cbf3ba33126ffd25ae7f2f6bbafb.function.png?foo=bar"`; exports[`"name" option should work with "Function" value: warnings 1`] = `Array []`; exports[`"name" option should work with "String" value: errors 1`] = `Array []`; -exports[`"name" option should work with "String" value: result 1`] = `"9c87cbf3ba33126ffd25ae7f2f6bbafb.string.png"`; +exports[`"name" option should work with "String" value: result 1`] = `"9c87cbf3ba33126ffd25ae7f2f6bbafb.string.png?foo=bar"`; exports[`"name" option should work with "String" value: warnings 1`] = `Array []`; diff --git a/test/fixtures/cdn.js b/test/fixtures/cdn.js new file mode 100644 index 0000000..2d24fca --- /dev/null +++ b/test/fixtures/cdn.js @@ -0,0 +1,6 @@ +/* eslint-disable */ +import png from './nested/file.png?foo=bar#hash'; + +__export__ = png; + +export default png; diff --git a/test/fixtures/nested/file.png b/test/fixtures/nested/file.png new file mode 100644 index 0000000000000000000000000000000000000000..02d7a6b2c756295d0dcff045a5cafa17a34cef4e GIT binary patch literal 6777 zcmV-<8iwVGP)Py4Gf6~2RCodHod>WK#n#9BlEs`+Q89o4%$T1FVkQ_76%dT*XMts@C8Cy;T7@X} z%m}FXVFEFLSrijSFd&LK=Pc@{0s{Ab{p&~X>)YG2JG*y#c6PT<)z<9H^vrah`OoRo z=bSz*M4-Tdz=6PlO5s4AtD-}P4qav8!i5*gbB8uGG<1;LR<3g4SWR%Cp{}m(7unL6 z<@WNMZo28EIIYeC){r4X_FlGZ+3gZg8^yUnC{sHekie>!xcK*m8*X^s$dM!KbG5v= zdGgd#PhGFRzW#Z6aXa&P_#Zg1oC64=J^oM98*jYvaxShkGfzrMHCBRZb@N#GA2?uh zKw{)FDTFq&kDL1$V9A1ONm19wJQ@B64n%OEjqKTui#Vqax6mhL!L1|`GeG0`YOAd# zF1+wUwbkXqwzfDRaUrHoohoL|oM|__vwgtaH-WUWd4m5NZM0DUmCckI+i%vandsa( z8$(iUYS*n>x6Sw6ci#>A_U*fZ%@z|__PMYhIFOSA&6_uGCe^1EfBp5>4G%u};I(Cs zm$5PgVC7_8I2o4%|NQgM>IWTkP-fs4fEAZ>pm zC%Z+Zs|R4kGZ%x4ftXS*{h2nCcb?eqjfe*ln%dt?dAPxjz6~wMkI4%bQ zu;Owmlq-k>0ayjGD-@2)fdH(yoC@U%;y?gaLF@{J<8mMq-4OSJS}G^q6k^e$MPljF zrJ_}nLZ)N56 zFTeaEo_OL3^#fP{Y}Z|P6-c&frm6xK-PZ6Z7D(I!JyzwYNx=w@;TR3oLIK>#{j9K8)4?S&U!P*SyP*RG;x&z@p~4K~PnRwW;( zXkdNy)mP$O>!V_0?CE1d~B>VwF`^i7H-E$14(8*p{YGpDvz$`e~<0zMVDy!X)?05q(W6HXvGdM5tFA3qSzqUnkbCdFxB6k4U_kNAGta1Q)b&h8t#{pZ zm+B@54H_gi-gskIv5GiWF~CB3zVpsI#fvY#n3J(gSwksii0}I9=|1K3aPih#Zz)N3 z=%I(^ykDtKVmL*L$q5JIx#yk}GiJ<)9I>LSjT$vd9C_rCV*ma3FI5#1y-!5|3-iIa zapMHrC_?!?>Zqdx-JMO^{P6nguZy34`YCf7r9ElFH3V4b?oi+#c;Er21VcBw`|i8N z2OoSOPB`I&vPs=y`ICBJp?jd9M~${H2OtUd+iyR?c<>&OrzP_D8lG6bTwMS0X__qyX>-y*le@SoLywpGkhP%v@u}70I}zud#VrU zi!Z*&dL%~HF=NIkaJeLM)&(gzkV;?yj>(fJt5KheM;I=78N2Vkd!y4BNVyI@3R+{0 zHIxF60*xY_WwcOuf68v9l8|V z0;4`ne9`yZb5G_P6y?JYKU{S)cK4{SN#hoC1-2}@=!p|2ia-DSvr#qS&8@fIs&ujg z4?M8ZxwP%13RrxlH{N)oK(BBS66*Nlj~6Xlw#=M<{`u#Xlw#C~Bx6Jf(Qy?ykQ2>f+pU&lO|Gj%_sf(CxqPzWbDw9^EXBlp0_$GD7;;ge|IHzkX`er_bRk z89#o!pbJEILW*Gstu{ge4;wa2b&IoS&(3NdAJ$4Mt<=b^lvSuq2O5vh%Tx(mGejlW zgno3=NhdV|6~@ggue?%$YST?ORb56kf=crOz3Y@yP7yK>l|Gc&(jD@F#r2hDE==H0 z*@5-(#~&+0psTI@`}bGQS$z(RHHK0`S8&%1A3j`#2kZ0Iyglx?eKLn1dgvi>`|YN zpt1mq5E7VR6A_&MjK$&Nx=u$0`W>i^# zMfi&yJC0d_5g<-ti1=_6b(qwvSFc9;R?zp{S=FXif z-hTV-tacx8zyYF5moDl!AtNaDM2g}UBdRh`HU}8s5V2|#rW=>;YM1l5pD3^x8Wa6# z6Las(fID0kL?*1i{`z9zz=3x0u)mxGNO|~!Qg0FtYn<`rq(p(m>IPWLZNhYbQVOA0 zVKBwlhhfn43)H6^A<7g#1K`vir z7Z3Xm4iM;wZ_y^qH07eifQ28Bxp*ca!cEh{V~;(iCP|<-pmPO^#{qnbgk{@=ZOe|1 zu*>+`PXt(u>>hvo@vJrwk)X4)kmUu5hTd@W(MM-Y4p|q415OSQ9m_mNn_OlcyNr+h zM1V!yI>0drE4pbxhXHh#IFu_Xw%cq|IsfxPn*7vtL1)zwwfa~R+eCoHY!sWAo~6Gq zX3hXU6n6Z8fr{e*JpdzYo0!X(G_7haLtuWeCukJ+gm_n#1z7g% zns`+WrQ&da>13`cVn{8*l#(K^z+$Lt&OD}3DD8Yzf&6j6_2H7)Fv%ozUV%kpY{Ge3 zEvb6!wxwmC)JjM$lNfQ66Cx?8<#jRzIihUB(w+p7HVD@c-hO;%c-vCwT)FUk42^ zi7t?c1)qfV9#FwJ;<)G*aBu_id+)thNe*J?332vGwEgO@jGr6+FcFdWx2a3w)5{m)gtm}5-;M>5a_GYWPQ3N*<-0J*NW zt6Wo#x%dsRAm58ABnm9w8T54bW?J>_7eiOXs0n?8uE7UU4*-Ksh21WS^fWGCz|y{0 z^Z6QBw*G6DDaHTjRyX19#9i561Y+Or^PBJp=cQV_=gNgeL@F*MKxK~BoHs1;Ai5ru zs~()!ADXZDbbWuGrAY&e?$DmlqRW(G>jhXp{`g~7-CSQ!R-q;yfRSYHTYK%bbKZ%t z9A3IFdkzr>=vDb5U^!1YU-m2)u*{Fir_}OdZa~MdDT(w#%(a7A`G(TO(j!&!m9yIr zcOPJ1bImn*@6X4ENrI2T2hl$6ttC0nKvL0m^gnn-wi+Mg1z2W7bftYUuR$_rnKVQ; z$kAZEw9nMZu5PJRjO*m2&gKzIx)BuT3skzjaqr(oO00UBdx3T|lPg}-;fl$jm+AY* z-P*w7IsgT*beA3XUWJsaSYS0sJ?Trvs9jYPEN?i5Q z%Dq^wWU)++0Naqcri7B9*cY+vn{UJ-X`qd&J1=^fG_3M#E@y?ek#@JbCUjRWV41oV zgBTxE?7S%NX2tajh}$*j@{wEll`WE1?Wh;yx^Af){v+MWP4zOfP9b$)CcDUm&pwM{ zRCLu{eAg0-oBA@jcmWo6BAXDqrJ!ZfqTf)2)tD}`+_XeSdqj2HQFTM=E?u5pJgQ8t z<1XNso&aAa2QR>KiC>pOpNk=p=Ze(_ioGtfB88u*FLLSNh9a+{hAzINxND;8^+6Q} ztmblv-mHkl09qEXk{13(@^a<-nm?CeD6Vcl>P0orV_z%vb==PdEVFyW{NRHs4p`D3 zBij7yU&^cymmw{rYo%r9&T)&YuokBJv!-+uefHUB;2Lis)bNX z8JNc?u~>2e-&EEsUSioUo?;orb*$Ba-X?Lbk~Oxm*A$c4Sth_HaJP|wYC zh5qPg>F8KiTa8V!3u-G@{%Rp-DCHaC*U7T2WwdSUCd>~2H06?5(QhR-x3UYmtnBur zitMWjJHimWwVYPoS|$|1{7^5uuLf*F(m>iwoT^Jvvt-SLgn1d@{602!7w_CD& zpOx8pF=qeG6npJTh`6k{!}7~+-oCxa9)}ipJFddjLTVruOSUVz0HfxnEHZ%up|?B{dL z>hk5@GSwgOuvir9CEA1|o79$RCKs@5hj&E*3ug1xo51224XqSlVe27FGZCIB@g&wj z;!=Mmgm&xJEw7$Lo6AXYfR!s=tSNQ(BC)6tAscqOZRWLA+VUhsqvnc8Z3^>!U+c;; zla$2rVs3z8t|(5{Hx85x2avAV*=(}o;(SRgZ@_Y`w@gD_v08TH!+z5_z-qJhh>+2` z9mQL5z@h>6*TE=}1_$!b0c~e9pQK~#&10sq)I8qQ|K21P6?4rabmensY9qq+*&J~3 znUSnbRRb)B@4k$3Hq&Z#pE(C#IHEahV9^a$Tvy0TWlo(LqQNnO`CVTV=K__61FmFu zjM7O+Vv_xC%yUis@3kvr7L02~va8`uZAiHOzZ`Hm&s`n@GkoU1SC{6WS71>^SK1(^ z!n?fgrD;-a-v!6H?U%*6?#l9KhR>9mYu-MsHw~%X2_C=u2_u`Q9ZK57}3)z-~7J2QTS_0+o&f zEZu8HlyGNqk`aYrlTT^Ivl0OoSx;P<>sUJ{m>%p`Ufe#J0$oZwOC6i>SNiV0(hKPO zCju-U*0X0%{mjf3d&{g@voibPPw^a>Hf>s#Y5{m$A2i*dPg4V}NDNqno48C51kEMA zYC`d^FP&H7+;>SU(sH{j>%OH`qQK%|y?gi0ieVxjCM&82DvkpTfLLSOCPI*1gC@%5 zORr?#tC;=7Bo>v!r9hu=6H2_#414zTs%f8OzASTW6Km?Y6m+DOJ&>}mEyaGKz@ow| z*l62SP~e$v<(g4aibhuJJ;+MvN{&k`oXf-jA8fadFWw{$EI{R2Iu0Amq)C%}wX^E( zi;;QUxN%vNYLP~+ff5X$3-qnDYepvyEFMheV%M}Ord#Q3!@7cD>*fHt>RmDIyUKZ# zb{w3(LsHU;=amImJecfzE+ulxlqt@T53ULH9}e*GxkiCRF!bor!^tLi?bGROKeTMX zVtUx1L4)l2DA7)uMOPlvtim;TVBWlW&QTzsVJ&S}SBDdNHJNKGNh%w#Xk^!}UDXN= zc7s_Kz4{(Q6UnEu#TWB8z74Os8OTTG?AhYF>#lP~+cNm+-@m`p7YJYAV>!A&{Z3K?EGoo0H$#UGb%Ki(vXET05W|K4 zZ|c;kYCS`H(kXl*f7if)1D)m?&LhFf)dli=C{@6sLd>9MeLvUNhP8R(#EI%>X8z^6 zY9Rt3M~@z@QYg9FL~PrMC!Xk38z_%yf&2_fj{@zwrVdzCutSFqV)*dk>dUk%%sx_! zG>xvL7q0S^a*R2bBS(%@Ik;SP#);3$u(`rskY?r5tIRs55?E9ax5S7MBO=ot;nBbJ z(o4nU$&)MIB!OuKqcFa|sFa#rx^xi(1`Nmv8thmu9o2l{N&WYh#;6nxU{rJd`R5B3 z4`(S{n~3jbRtjG_R%iTVl|<-I#Hkaa9hIS(E^U9Ax0I!aa}k#Hg7Tr}x>*U%r5aeY z53xRY@L-h(0>`UsJ|c=e3wO_$F+(lt?Fz>&0k2AS3-m)mvYDx5F7swT00o>dBNo)m zMU>0cJ?6UB*SXXKOE;V^7TpV%MwD)a&1>w~u__rYM#EiX^4lmIkG^k`Tb&e_;5?UI z%B&0N3HtQule23>QAejrOZR8izhZzzrSaZhbkRj(=FFMm!3Q6V3=Bm7y7}gtRmd~l zBeCD*N>ND==3{Tv&W}WQ+6$dQc0KROJ&yrw_sZaS9?8b@9j39m}xt%(7QhFO8b7f-D&xy0e z_QV3?r0OP<4qKOD({(QZPO9(j-MdGcSIp~(z)d;?#(aL<{}l@?-9VDNF>+)I)b#1o zm88(;qP7g_$%f2N6JP^im^n~{(Oc+d*dieqG!T=ow>ZWLr{-6Tu@=e8bhy3t+A9{I z(yUKu9-7;V2bOLOI>~wGou`1s5FW`9S88w%z=ovZ=aT?1Aue=#m{Z~qK&Nb)1|s?D zT{Xbc4ei*mqjF|5y#5Iw*If-dzL~Y`N;fFMJ#rNyF+FO8#uQYA8DZ8Gz_Nx|HG#b5x z7mRY;LE6C3i-;W1iZ-)ci1R|aMOnUcF;AbPd&8Z=NDZ?FV2TP8*4MbUNGR%C2nb#G zNyD+K29{SH0odG6*RV-0A zfW$qxXSGmG0876tULxS)hvWie`~VNiaa?6u->)fKKnj@nQLk#FngW*j@=&1BWB36w z^e!Y7TlPy$TH+y`YuuN{m{12G4go=4d%@s>+1ZmB^ zi7IPw09KW~(6lrs04ptTqRQ$KfK_ELG%d{uz)H)TsIq#Pz{*Mvfb%w2uz!{DA~eUr z0ql0*EF8c6L#5r|111$EWwfTRfh+B~Bf^aTyK)SiETCiZjSGhRR2v`#) zO!!WM>o5tfFLPZK&ebLdBrfWu&-4~Kuk(NNai9^fC@d$0&XW4pHu5G{%HN0b&_DTZ z63$mO2O4DSewAv{)3WTp+sc@w_BrnoP6iGv=YRy=pK_-6 zw^)nip7H zBd2};AOqIw0|x^Ka&bTc>le91pz7iB>R9?l=vvZ%+D=NZj8Rpe3){c}8E(BmP89xG z4)~N}9~)S(u5aJI&E?2o4H>$-lKiy{?5u4LNDS0VDYa0};rvxfs)e=9jnMkQfxv;v b<-q>~hRJINZ^rzJ00000NkvXXu0mjf%EBh% literal 0 HcmV?d00001 diff --git a/test/fixtures/simple.js b/test/fixtures/simple.js index f1a0830..b867552 100644 --- a/test/fixtures/simple.js +++ b/test/fixtures/simple.js @@ -1,5 +1,5 @@ /* eslint-disable */ -import png from './file.png'; +import png from './file.png?foo=bar'; __export__ = png; diff --git a/test/name-option.test.js b/test/name-option.test.js index 1f46850..22ca42c 100644 --- a/test/name-option.test.js +++ b/test/name-option.test.js @@ -1,3 +1,5 @@ +import path from 'path'; + import { compile, execute, @@ -22,7 +24,7 @@ describe('"name" option', () => { it('should work with "String" value', async () => { const compiler = getCompiler('simple.js', { - name: '[hash].string.[ext]', + name: '[hash].string.[ext][query]', }); const stats = await compile(compiler); @@ -37,8 +39,11 @@ describe('"name" option', () => { it('should work with "Function" value', async () => { const compiler = getCompiler('simple.js', { - name() { - return '[hash].function.[ext]'; + name(resourcePath, resourceQuery) { + expect(resourcePath).toBeDefined(); + expect(resourceQuery).toBeDefined(); + + return '[hash].function.[ext][query]'; }, }); const stats = await compile(compiler); @@ -51,4 +56,30 @@ describe('"name" option', () => { ); expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); }); + + it('should work for CDN support query params', async () => { + const compiler = getCompiler( + 'cdn.js', + { + name: '[path][name].[ext][query]', + }, + { + output: { + path: path.resolve(__dirname, './outputs'), + publicPath: 'https://cdn.example.com/', + filename: '[name].bundle.js', + chunkFilename: '[name].chunk.js', + }, + } + ); + const stats = await compile(compiler); + + expect( + execute(readAsset('main.bundle.js', compiler, stats)) + ).toMatchSnapshot('result'); + expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot( + 'warnings' + ); + expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); + }); });