From 154d0bad5b47cea23fce05a0779240f060bf0960 Mon Sep 17 00:00:00 2001 From: Kevin Meijer Date: Wed, 18 Mar 2026 13:41:25 +0100 Subject: [PATCH 1/5] Updated to Statamic 6 --- README.md | 28 +- composer.json | 22 +- dist/js/statamic-image-optimize.js | 3 +- .../js/statamic-image-optimize.js.LICENSE.txt | 1 - package.json | 16 +- resources/js/Pages/ImageResize/Index.vue | 235 ++++++++ .../components/cp/image-resize/ResizeForm.vue | 153 ----- resources/js/statamic-image-optimize.js | 4 +- .../views/cp/image-resize/index.blade.php | 5 - routes/cp.php | 5 +- src/Actions/ResizeImage.php | 21 +- src/Actions/ResizeImages.php | 7 +- .../Controllers/CP/ImageResizeController.php | 53 +- src/ServiceProvider.php | 2 +- tests/Actions/ResizeImageTest.php | 30 +- tests/Actions/ResizeImagesTest.php | 7 +- .../Controllers/ImageResizeControllerTest.php | 55 +- tests/Listeners/AssetUploadedListenerTest.php | 2 +- tests/TestCase.php | 32 +- vite.config.js | 18 + webpack.mix.js | 3 - yarn.lock | 550 ++++++++++++++++++ 22 files changed, 979 insertions(+), 273 deletions(-) delete mode 100644 dist/js/statamic-image-optimize.js.LICENSE.txt create mode 100644 resources/js/Pages/ImageResize/Index.vue delete mode 100644 resources/js/components/cp/image-resize/ResizeForm.vue delete mode 100644 resources/views/cp/image-resize/index.blade.php create mode 100644 vite.config.js delete mode 100644 webpack.mix.js create mode 100644 yarn.lock diff --git a/README.md b/README.md index eada5f8..2a3fc0d 100644 --- a/README.md +++ b/README.md @@ -10,39 +10,42 @@ You can search for this addon in the `Tools > Addons` section of the Statamic control panel and click **install**, or run the following command from your project root: -``` bash +```bash composer require justbetter/statamic-image-optimize ``` ## Requirements -The addon makes use of batches to optimize the images. -Because of this you need an active Database connection that contains the `job_batches` table. + +The addon makes use of **Laravel Batches** to optimize images. +Because of this you need an active database connection that contains the `job_batches` table. You can generate this table by running the following commands: -``` +```bash php artisan queue:batches-table php artisan migrate ``` +If your queue connection is not `sync`, make sure a queue worker is running. + ## Config ### Publish -``` +```bash php artisan vendor:publish --provider="JustBetter\ImageOptimize\ServiceProvider" ``` ### Settings -It's possible to change to default resize width and height by overriding the config file and changing the parameters within. - +You can change the default resize width/height and queue settings in `config/image-optimize.php` (or via env vars like `IMAGE_OPTIMIZE_WIDTH`, `IMAGE_OPTIMIZE_HEIGHT`, `IMAGE_OPTIMIZE_QUEUE_CONNECTION`, `IMAGE_OPTIMIZE_QUEUE_NAME`). ## Commands -``` + +```bash php artisan justbetter:optimize:images ``` -By running this command you can recursively optimize all the images in the assets folder. +By running this command you can optimize images in the Statamic asset library. ### Options @@ -56,8 +59,7 @@ this will show a progress bar containing the amount of jobs left in the batch. - After an image is uploaded an event will trigger to optimize the image. The event optimizes the images and resizes it to a specified size, this is being controlled by the config file. - -- By using the resize images command you can recursively optimize all the images in the assets folder. - +- By using the optimize images command you can optimize images in the asset library. - Added an action in the CP Asset overview that allows you to select assets and trigger the optimize job manually. -- Added an CP page to manually optimize all images, triggering this will show a progress bar containing the remaining images. +- Added a CP page to optimize remaining images or force-optimize all images, showing batch progress while it runs. + diff --git a/composer.json b/composer.json index ffbe6ee..65e8fd4 100644 --- a/composer.json +++ b/composer.json @@ -15,19 +15,20 @@ } ], "require": { - "php": "^8.2|^8.3", + "php": "^8.4|^8.5", "ext-fileinfo": "*", - "statamic/cms": "^5.0", - "laravel/framework": "^11.0 || ^12.0", - "league/glide": "^2.3" + "intervention/image": "^3.0", + "statamic/cms": "^6.0", + "laravel/framework": "^12.0", + "league/glide": "^3.0" }, "require-dev": { "laravel/pint": "^1.7", - "larastan/larastan": "^2.5", - "phpstan/phpstan-mockery": "^1.1", - "phpunit/phpunit": "^10.1 || ^11.5", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.0" + "larastan/larastan": "^3.4", + "phpstan/phpstan-mockery": "^2.0", + "phpunit/phpunit": "^11.5", + "orchestra/testbench": "^10.3", + "pestphp/pest": "^3.7" }, "autoload": { "psr-4": { @@ -41,8 +42,9 @@ }, "scripts": { "test": "phpunit", - "analyse": "phpstan", + "analyse": "phpstan --memory-limit=1G", "style": "pint --test", + "coverage": "XDEBUG_MODE=coverage php vendor/bin/pest --coverage --min=100", "quality": [ "@test", "@analyse", diff --git a/dist/js/statamic-image-optimize.js b/dist/js/statamic-image-optimize.js index 9f3130c..fa8a3b7 100644 --- a/dist/js/statamic-image-optimize.js +++ b/dist/js/statamic-image-optimize.js @@ -1,2 +1 @@ -/*! For license information please see statamic-image-optimize.js.LICENSE.txt */ -(()=>{"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(){e=function(){return r};var r={},n=Object.prototype,o=n.hasOwnProperty,i=Object.defineProperty||function(t,e,r){t[e]=r.value},s="function"==typeof Symbol?Symbol:{},a=s.iterator||"@@iterator",c=s.asyncIterator||"@@asyncIterator",u=s.toStringTag||"@@toStringTag";function l(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{l({},"")}catch(t){l=function(t,e,r){return t[e]=r}}function f(t,e,r,n){var o=e&&e.prototype instanceof p?e:p,s=Object.create(o.prototype),a=new k(n||[]);return i(s,"_invoke",{value:C(t,r,a)}),s}function h(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}r.wrap=f;var d={};function p(){}function v(){}function m(){}var g={};l(g,a,(function(){return this}));var y=Object.getPrototypeOf,b=y&&y(y(L([])));b&&b!==n&&o.call(b,a)&&(g=b);var w=m.prototype=p.prototype=Object.create(g);function x(t){["next","throw","return"].forEach((function(e){l(t,e,(function(t){return this._invoke(e,t)}))}))}function _(e,r){function n(i,s,a,c){var u=h(e[i],e,s);if("throw"!==u.type){var l=u.arg,f=l.value;return f&&"object"==t(f)&&o.call(f,"__await")?r.resolve(f.__await).then((function(t){n("next",t,a,c)}),(function(t){n("throw",t,a,c)})):r.resolve(f).then((function(t){l.value=t,a(l)}),(function(t){return n("throw",t,a,c)}))}c(u.arg)}var s;i(this,"_invoke",{value:function(t,e){function o(){return new r((function(r,o){n(t,e,r,o)}))}return s=s?s.then(o,o):o()}})}function C(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return E()}for(r.method=o,r.arg=i;;){var s=r.delegate;if(s){var a=z(s,r);if(a){if(a===d)continue;return a}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var c=h(t,e,r);if("normal"===c.type){if(n=r.done?"completed":"suspendedYield",c.arg===d)continue;return{value:c.arg,done:r.done}}"throw"===c.type&&(n="completed",r.method="throw",r.arg=c.arg)}}}function z(t,e){var r=e.method,n=t.iterator[r];if(void 0===n)return e.delegate=null,"throw"===r&&t.iterator.return&&(e.method="return",e.arg=void 0,z(t,e),"throw"===e.method)||"return"!==r&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+r+"' method")),d;var o=h(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,d;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,d):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,d)}function j(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function O(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function k(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(j,this),this.reset(!0)}function L(t){if(t){var e=t[a];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,n=function e(){for(;++r=0;--n){var i=this.tryEntries[n],s=i.completion;if("root"===i.tryLoc)return r("end");if(i.tryLoc<=this.prev){var a=o.call(i,"catchLoc"),c=o.call(i,"finallyLoc");if(a&&c){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&o.call(n,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),O(r),d}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;O(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:L(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),d}},r}function r(t,e,r,n,o,i,s){try{var a=t[i](s),c=a.value}catch(t){return void r(t)}a.done?e(c):Promise.resolve(c).then(n,o)}function n(t){return function(){var e=this,n=arguments;return new Promise((function(o,i){var s=t.apply(e,n);function a(t){r(s,o,i,a,c,"next",t)}function c(t){r(s,o,i,a,c,"throw",t)}a(void 0)}))}}var o=function(t,e,r,n,o,i,s,a){var c,u="function"==typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=r,u._compiled=!0),n&&(u.functional=!0),i&&(u._scopeId="data-v-"+i),s?(c=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(s)},u._ssrRegister=c):o&&(c=a?function(){o.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var l=u.render;u.render=function(t,e){return c.call(e),l(t,e)}}else{var f=u.beforeCreate;u.beforeCreate=f?[].concat(f,c):[c]}return{exports:t,options:u}}({data:function(){return{batchId:null,jobCount:this.unoptimizedAssets,currentJobCount:this.unoptimizedAssets,jobsDone:0,checkJobs:!1,jobStarted:!1,resizeUrl:"/cp/statamic-image-optimize/resize-images/",resizeAllUrl:"/cp/statamic-image-optimize/resize-images/force-all",resizeCheckUrl:"/cp/statamic-image-optimize/resize-images-count/"}},props:{title:String,buttonText:String,totalAssets:Number,unoptimizedAssets:Number,canOptimize:Number},computed:{loadingMessage:function(){return this.jobStarted?(this.jobsDone=this.jobCount-this.currentJobCount,this.jobsDone+" of "+this.jobCount+" images have been optimized."):""},checkAllDisabled:function(){return this.unoptimizedAssets>0},canOptimizeAssets:function(){return this.canOptimize>=1}},methods:{onTriggerResizeImages:function(){var t=arguments,r=this;return n(e().mark((function o(){var i,s,a;return e().wrap((function(o){for(;;)switch(o.prev=o.next){case 0:return i=t.length>0&&void 0!==t[0]&&t[0],s=r,r.jobStarted=!0,r.checkJobs=!0,o.next=6,r.resizeImages(i);case 6:a=setInterval(n(e().mark((function t(){var r;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,s.checkResizeImages();case 2:if(void 0!==(r=t.sent).assetsToOptimize&&void 0!==r.assetTotal){t.next=7;break}return this.checkJobs=!1,clearInterval(a),t.abrupt("return");case 7:s.jobCount=r.assetTotal,s.currentJobCount=r.assetsToOptimize,0===r.assetsToOptimize&&(this.checkJobs=!1,clearInterval(a));case 10:case"end":return t.stop()}}),t,this)}))),1e3);case 7:case"end":return o.stop()}}),o)})))()},resizeImages:function(){var t=arguments,r=this;return n(e().mark((function n(){var o,i;return e().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return o=t.length>0&&void 0!==t[0]&&t[0],i=r,e.next=4,fetch(o?r.resizeAllUrl:r.resizeUrl).then((function(t){return t.json()})).then((function(t){return i.batchId=t.batchId,i.checkJobs=!1,t})).catch((function(t){console.error(t)}));case 4:return e.abrupt("return",e.sent);case 5:case"end":return e.stop()}}),n)})))()},checkResizeImages:function(){var t=this;return n(e().mark((function r(){return e().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(t.resizeCheckUrl+t.batchId).then((function(t){return t.json()})).then((function(t){return t})).catch((function(t){console.error(t)}));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),r)})))()}}},(function(){var t=this,e=t._self._c;return e("div",[e("div",{staticClass:"flex items-center justify-between mb-3"},[e("h1",{domProps:{textContent:t._s(t.title)}}),t._v(" "),t.canOptimizeAssets?e("div",[e("button",{staticClass:"btn-primary",attrs:{disabled:t.checkJobs||!t.checkAllDisabled},on:{click:function(e){return t.onTriggerResizeImages(!1)}}},[t._v("Optimize remaining images")]),t._v(" "),e("button",{staticClass:"btn-primary",attrs:{disabled:t.checkJobs},on:{click:function(e){return t.onTriggerResizeImages(!0)}}},[t._v("Optimize all images")])]):t._e()]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:!t.canOptimizeAssets,expression:"!canOptimizeAssets"}],staticClass:"mt-2"},[t._m(0)]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:!t.loadingMessage&&t.canOptimizeAssets,expression:"!loadingMessage && canOptimizeAssets"}],staticClass:"mt-2"},[e("div",{staticClass:"w-full mb-2"},[e("div",{staticClass:"mt-2 grid grid-cols-1 gap-5 sm:grid-cols-2"},[e("div",{staticClass:"overflow-hidden rounded-lg bg-white shadow"},[e("div",{staticClass:"flex flex-col justify-between items-center w-full h-full p-5"},[e("div",{staticClass:"truncate text-xl text-gray-500"},[t._v("\n Total amount of images\n ")]),t._v(" "),e("div",{staticClass:"text-5xl font-medium text-gray-900",domProps:{textContent:t._s(t.totalAssets)}})])]),t._v(" "),e("div",{staticClass:"overflow-hidden rounded-lg bg-white shadow"},[e("div",{staticClass:"flex flex-col justify-between items-center w-full h-full p-5"},[e("div",{staticClass:"truncate text-xl text-gray-500"},[t._v("\n Images to optimize\n ")]),t._v(" "),e("div",{staticClass:"text-5xl font-medium text-gray-900",domProps:{textContent:t._s(t.unoptimizedAssets)}})])])])])]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:t.loadingMessage&&t.canOptimizeAssets,expression:"loadingMessage && canOptimizeAssets"}],staticClass:"mt-2"},[e("div",{staticClass:"w-full mb-2"},[e("div",{staticClass:"mt-2 grid grid-cols-1"},[e("div",{staticClass:"overflow-hidden rounded-lg bg-white shadow"},[e("div",{staticClass:"flex flex-col justify-between items-center w-full h-full p-5"},[e("div",{staticClass:"truncate text-xl text-gray-500",domProps:{textContent:t._s(t.loadingMessage)}})])])])])])])}),[function(){var t=this._self._c;return t("ul",{staticClass:"card p-0 mb-2"},[t("li",{staticClass:"flex items-center justify-between py-1 px-2 border-b group"},[this._v("\n You need an active database connection in order to use the optimize addon.\n ")])])}],!1,null,null,null);const i=o.exports;Statamic.booting((function(){Statamic.component("justbetter-statamic-optimize-image-form",i)}))})(); \ No newline at end of file +const B=window.Vue,{BaseTransition:Z,BaseTransitionPropsValidators:ee,Comment:te,DeprecationTypes:oe,EffectScope:ne,ErrorCodes:ae,ErrorTypeStrings:ie,Fragment:re,KeepAlive:se,ReactiveEffect:le,Static:ce,Suspense:de,Teleport:ue,Text:pe,TrackOpTypes:me,Transition:ge,TransitionGroup:he,TriggerOpTypes:ve,VueElement:fe,__esModule:Ce,assertNumber:Se,callWithAsyncErrorHandling:be,callWithErrorHandling:ye,camelize:Te,capitalize:xe,cloneVNode:we,compatUtils:Re,compile:Pe,computed:R,createApp:ke,createBlock:C,createCommentVNode:A,createElementBlock:M,createElementVNode:r,createHydrationRenderer:Ie,createPropsRestProxy:_e,createRenderer:Ae,createSSRApp:Me,createSlots:ze,createStaticVNode:Ee,createTextVNode:He,createVNode:S,customRef:Le,defineAsyncComponent:De,defineComponent:Fe,defineCustomElement:Be,defineEmits:Oe,defineExpose:Ne,defineModel:Ve,defineOptions:Ue,defineProps:qe,defineSSRCustomElement:Ge,defineSlots:je,devtools:Ke,effect:Xe,effectScope:Je,getCurrentInstance:We,getCurrentScope:$e,getCurrentWatcher:Qe,getTransitionRawChildren:Ye,guardReactiveProps:Ze,h:et,handleError:tt,hasInjectionContext:ot,hydrate:nt,hydrateOnIdle:at,hydrateOnInteraction:it,hydrateOnMediaQuery:rt,hydrateOnVisible:st,initCustomFormatter:lt,initDirectivesForSSR:ct,inject:dt,isMemoSame:ut,isProxy:pt,isReactive:mt,isReadonly:gt,isRef:ht,isRuntimeOnly:vt,isShallow:ft,isVNode:Ct,markRaw:St,mergeDefaults:bt,mergeModels:yt,mergeProps:Tt,nextTick:xt,nodeOps:wt,normalizeClass:Rt,normalizeProps:Pt,normalizeStyle:kt,onActivated:It,onBeforeMount:_t,onBeforeUnmount:O,onBeforeUpdate:At,onDeactivated:Mt,onErrorCaptured:zt,onMounted:Et,onRenderTracked:Ht,onRenderTriggered:Lt,onScopeDispose:Dt,onServerPrefetch:Ft,onUnmounted:Bt,onUpdated:Ot,onWatcherCleanup:Nt,openBlock:d,patchProp:Vt,popScopeId:Ut,provide:qt,proxyRefs:Gt,pushScopeId:jt,queuePostFlushCb:Kt,reactive:Xt,readonly:Jt,ref:s,registerRuntimeCompiler:Wt,render:$t,renderList:Qt,renderSlot:Yt,resolveComponent:Zt,resolveDirective:eo,resolveDynamicComponent:to,resolveFilter:oo,resolveTransitionHooks:no,setBlockTracking:ao,setDevtoolsHook:io,setTransitionHooks:ro,shallowReactive:so,shallowReadonly:lo,shallowRef:co,ssrContextKey:uo,ssrUtils:po,stop:mo,toDisplayString:u,toHandlerKey:go,toHandlers:ho,toRaw:vo,toRef:fo,toRefs:Co,toValue:So,transformVNodeArgs:bo,triggerRef:yo,unref:l,useAttrs:To,useCssModule:xo,useCssVars:wo,useHost:Ro,useId:Po,useModel:ko,useSSRContext:Io,useShadowRoot:_o,useSlots:Ao,useTemplateRef:Mo,useTransitionState:zo,vModelCheckbox:Eo,vModelDynamic:Ho,vModelRadio:Lo,vModelSelect:Do,vModelText:Fo,vShow:Bo,version:Oo,warn:No,watch:Vo,watchEffect:Uo,watchPostEffect:qo,watchSyncEffect:Go,withAsyncContext:jo,withCtx:m,withDefaults:Ko,withDirectives:Xo,withKeys:Jo,withMemo:Wo,withModifiers:$o,withScopeId:Qo}=B,{Form:Yo,Head:N,Link:Zo,router:en,toggleArchitecturalBackground:tn,useArchitecturalBackground:on,useForm:nn,usePoll:an}=__STATAMIC__.inertia,{Alert:rn,AuthCard:sn,Avatar:ln,Badge:cn,Button:z,ButtonGroup:dn,Calendar:un,Card:b,CardList:pn,CardListItem:mn,CardPanel:gn,CharacterCounter:hn,Checkbox:vn,CheckboxGroup:fn,CodeEditor:Cn,Combobox:Sn,CommandPaletteItem:bn,ConfirmationModal:yn,Context:Tn,ContextFooter:xn,ContextHeader:wn,ContextItem:Rn,ContextLabel:Pn,ContextMenu:kn,ContextSeparator:In,CreateForm:_n,DatePicker:An,DateRangePicker:Mn,Description:zn,DocsCallout:En,DragHandle:Hn,Dropdown:Ln,DropdownItem:Dn,DropdownLabel:Fn,DropdownMenu:Bn,DropdownSeparator:On,DropdownFooter:Nn,DropdownHeader:Vn,Editable:Un,ErrorMessage:qn,EmptyStateItem:Gn,EmptyStateMenu:jn,Field:Kn,Header:V,Heading:Xn,HoverCard:Jn,Icon:Wn,Input:$n,InputGroup:Qn,InputGroupAppend:Yn,InputGroupPrepend:Zn,Label:ea,Listing:ta,ListingCustomizeColumns:oa,ListingFilters:na,ListingHeaderCell:aa,ListingPagination:ia,ListingPresets:ra,ListingPresetTrigger:sa,ListingRowActions:la,ListingSearch:ca,ListingTable:da,ListingTableBody:ua,ListingTableHead:pa,ListingToggleAll:ma,LivePreview:ga,LivePreviewPopout:ha,MiddleEllipsis:va,Modal:fa,ModalClose:Ca,ModalTitle:Sa,Pagination:ba,Panel:ya,PanelFooter:Ta,PanelHeader:xa,Popover:wa,PublishComponents:Ra,PublishContainer:Pa,publishContextKey:ka,injectPublishContext:Ia,PublishField:_a,PublishFields:Aa,PublishFieldsProvider:Ma,PublishForm:za,PublishLocalizations:Ea,PublishSections:Ha,PublishTabs:La,Radio:Da,RadioGroup:Fa,Select:Ba,Separator:Oa,Slider:Na,Skeleton:Va,SplitterGroup:Ua,SplitterPanel:qa,SplitterResizeHandle:Ga,StatusIndicator:ja,Subheading:Ka,Switch:Xa,TabContent:Ja,Stack:Wa,StackClose:$a,StackHeader:Qa,StackFooter:Ya,StackContent:Za,Table:ei,TableCell:ti,TableColumn:oi,TableColumns:ni,TableRow:ai,TableRows:ii,TabList:ri,TabProvider:si,Tabs:li,TabTrigger:ci,Textarea:di,TimePicker:ui,ToggleGroup:pi,ToggleItem:mi,Widget:gi,registerIconSet:hi,registerIconSetFromStrings:vi}=__STATAMIC__.ui,U={class:"max-w-5xl 3xl:max-w-6xl mx-auto","data-max-width-wrapper":""},q={class:"space-y-6"},G={class:"text-sm text-gray-700"},j={class:"flex flex-col items-center gap-3 py-6"},K=["textContent"],X={key:2,class:"grid grid-cols-1 gap-4 sm:grid-cols-2"},J={class:"text-sm text-gray-600 mb-1"},W=["textContent"],$={class:"text-sm text-gray-600 mb-1"},Q=["textContent"],Y={__name:"Index",props:{totalAssets:{type:Number,required:!0},unoptimizedAssets:{type:Number,required:!0},canOptimize:{type:Boolean,required:!0},startBatchUrl:{type:String,required:!0},batchStatusUrlTemplate:{type:String,required:!0}},setup(i){const y=s(null),g=s(i.unoptimizedAssets),h=s(0),p=s(0),T=s(i.unoptimizedAssets),c=s(!1),P=s(!1),v=s(null),x=R(()=>i.canOptimize===!0),E=R(()=>i.unoptimizedAssets>0),k=R(()=>{if(!P.value)return"";const e=__(":done of :total images have been optimized.",{done:h.value,total:g.value});return p.value>0?`${e} ${__("(:failed failed)",{failed:p.value})}`:e}),H=e=>{var n;const a=`; ${document.cookie}`.split(`; ${e}=`);return a.length!==2?null:((n=a.pop())==null?void 0:n.split(";").shift())??null},L=()=>{var n;const e=((n=document.querySelector('meta[name="csrf-token"]'))==null?void 0:n.getAttribute("content"))??null,o=H("XSRF-TOKEN"),a=o?decodeURIComponent(o):null;return{...e?{"X-CSRF-TOKEN":e}:{},...a?{"X-XSRF-TOKEN":a}:{}}},D=e=>i.batchStatusUrlTemplate.replace("__BATCH_ID__",e),F=async e=>{try{const o=L(),n=await(await fetch(i.startBatchUrl,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",...o},body:JSON.stringify({scope:e})})).json().catch(()=>null);return y.value=(n==null?void 0:n.batchId)??null,n}catch(o){return console.error(o),null}},I=async()=>{if(!y.value)return null;try{const e=await fetch(D(y.value),{credentials:"same-origin",headers:{Accept:"application/json","X-Requested-With":"XMLHttpRequest"}});return e.ok?await e.json().catch(()=>null):null}catch(e){return console.error(e),null}},f=()=>{v.value&&(window.clearInterval(v.value),v.value=null)},_=async(e=!1)=>{P.value=!0,c.value=!0,f(),g.value=0,h.value=0,p.value=0,T.value=0;const a=await F(e?"all":"remaining");if(!(a!=null&&a.batchId)){c.value=!1;return}const n=t=>{g.value=(t==null?void 0:t.total)??g.value,h.value=(t==null?void 0:t.processed)??h.value,p.value=(t==null?void 0:t.failed)??p.value,T.value=(t==null?void 0:t.pending)??T.value},w=await I();if(w&&(n(w),w.finished===!0)){c.value=!1;return}v.value=window.setInterval(async()=>{const t=await I();if(!t){c.value=!1,f();return}n(t),t.finished===!0&&(c.value=!1,f())},1e3)};return O(()=>{f()}),(e,o)=>(d(),M("div",U,[S(l(N),{title:e.__("Image Optimize")},null,8,["title"]),S(l(V),{title:e.__("Image Optimize"),icon:"collection"},{default:m(()=>[x.value?(d(),C(l(z),{key:0,disabled:c.value||!E.value,variant:"primary",text:e.__("Optimize remaining images"),onClick:o[0]||(o[0]=a=>_(!1))},null,8,["disabled","text"])):A("",!0),x.value?(d(),C(l(z),{key:1,disabled:c.value,text:e.__("Optimize all images"),onClick:o[1]||(o[1]=a=>_(!0))},null,8,["disabled","text"])):A("",!0)]),_:1},8,["title"]),r("div",q,[x.value?k.value?(d(),C(l(b),{key:1},{default:m(()=>[r("div",j,[r("div",{class:"text-sm text-gray-700",textContent:u(k.value)},null,8,K)])]),_:1})):(d(),M("div",X,[S(l(b),null,{default:m(()=>[r("div",J,u(e.__("Total amount of images")),1),r("div",{class:"text-3xl font-bold",textContent:u(i.totalAssets)},null,8,W)]),_:1}),S(l(b),null,{default:m(()=>[r("div",$,u(e.__("Images to optimize")),1),r("div",{class:"text-3xl font-bold",textContent:u(i.unoptimizedAssets)},null,8,Q)]),_:1})])):(d(),C(l(b),{key:0},{default:m(()=>[r("div",G,u(e.__("You need an active database connection in order to use the optimize addon.")),1)]),_:1}))])]))}};Statamic.booting(()=>{Statamic.$inertia.register("statamic-image-optimize::ImageResize/Index",Y)}); diff --git a/dist/js/statamic-image-optimize.js.LICENSE.txt b/dist/js/statamic-image-optimize.js.LICENSE.txt deleted file mode 100644 index ae386fb..0000000 --- a/dist/js/statamic-image-optimize.js.LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ diff --git a/package.json b/package.json index 7561a37..70bceb2 100644 --- a/package.json +++ b/package.json @@ -3,19 +3,17 @@ "private": true, "description": "Image optimization after upload", "scripts": { - "development": "mix", - "watch": "mix watch", - "production": "mix --production" + "dev": "vite", + "build": "vite build", + "preview": "vite preview" }, "author": "JustBetter", "dependencies": { - "vue": "^2.6.12", - "vue-loader": "^15.9.8", - "vue-template-compiler": "^2.6.12" + "vue": "^3.5.0" }, "devDependencies": { - "cross-env": "^7.0.3", - "laravel-mix": "^6.0.49", - "postcss": "^8.4.21" + "@statamic/cms": "file:./vendor/statamic/cms/resources/dist-package", + "postcss": "^8.4.21", + "vite": "^6.0.0" } } diff --git a/resources/js/Pages/ImageResize/Index.vue b/resources/js/Pages/ImageResize/Index.vue new file mode 100644 index 0000000..149b7a0 --- /dev/null +++ b/resources/js/Pages/ImageResize/Index.vue @@ -0,0 +1,235 @@ + + + + diff --git a/resources/js/components/cp/image-resize/ResizeForm.vue b/resources/js/components/cp/image-resize/ResizeForm.vue deleted file mode 100644 index 9835224..0000000 --- a/resources/js/components/cp/image-resize/ResizeForm.vue +++ /dev/null @@ -1,153 +0,0 @@ - - diff --git a/resources/js/statamic-image-optimize.js b/resources/js/statamic-image-optimize.js index 61e4da6..3f4d561 100644 --- a/resources/js/statamic-image-optimize.js +++ b/resources/js/statamic-image-optimize.js @@ -1,5 +1,5 @@ -import ResizeForm from './components/cp/image-resize/ResizeForm'; +import ImageResizeIndex from './Pages/ImageResize/Index.vue'; Statamic.booting(() => { - Statamic.component('justbetter-statamic-optimize-image-form', ResizeForm); + Statamic.$inertia.register('statamic-image-optimize::ImageResize/Index', ImageResizeIndex); }); \ No newline at end of file diff --git a/resources/views/cp/image-resize/index.blade.php b/resources/views/cp/image-resize/index.blade.php deleted file mode 100644 index 18b1154..0000000 --- a/resources/views/cp/image-resize/index.blade.php +++ /dev/null @@ -1,5 +0,0 @@ -@extends('statamic::layout') - -@section('content') - -@stop diff --git a/routes/cp.php b/routes/cp.php index a96dee0..e6892ea 100644 --- a/routes/cp.php +++ b/routes/cp.php @@ -8,6 +8,7 @@ ->controller(ImageResizeController::class) ->group(function () { Route::get('/', 'index')->name('index'); - Route::get('/resize-images/{forceAll?}', 'resizeImages')->name('resize-images'); - Route::get('/resize-images-count/{batchId?}', 'resizeImagesJobCount')->name('resize-images-count'); + + Route::post('/batches', 'startBatch')->name('batches.start'); + Route::get('/batches/{batchId}', 'batchStatus')->name('batches.status'); }); diff --git a/src/Actions/ResizeImage.php b/src/Actions/ResizeImage.php index d9ff519..248ac28 100644 --- a/src/Actions/ResizeImage.php +++ b/src/Actions/ResizeImage.php @@ -2,11 +2,11 @@ namespace JustBetter\ImageOptimize\Actions; -use Intervention\Image\Exception\NotReadableException; -use Intervention\Image\Facades\Image; +use Intervention\Image\Exceptions\DriverException; +use Intervention\Image\Exceptions\RuntimeException; +use Intervention\Image\ImageManager; use JustBetter\ImageOptimize\Contracts\ResizesImage; use JustBetter\ImageOptimize\Events\ImageResizedEvent; -use League\Glide\Manipulators\Size; use Statamic\Assets\Asset; class ResizeImage implements ResizesImage @@ -23,19 +23,24 @@ public function resize(Asset $asset, ?int $width = null, ?int $height = null): v $width ??= (int) config('image-optimize.default_resize_width'); $height ??= (int) config('image-optimize.default_resize_height'); - // Prevents exceptions occurring when resizing non-compatible filetypes like SVG. try { - $orientedImage = Image::make($asset->resolvedPath())->orientate(); + $manager = ImageManager::gd(); - $image = (new Size)->runMaxResize($orientedImage, $width, $height); + $image = $manager->read($asset->resolvedPath()) + ->scaleDown($width, $height); - $asset->disk()->filesystem()->put($asset->path(), $image->encode()); + $extension = $asset->extension(); + + $asset->disk()->filesystem()->put( + $asset->path(), + $image->encodeByExtension($extension) + ); $asset->merge(['image-optimized' => '1']); $asset->save(); $asset->meta(); - } catch (NotReadableException) { + } catch (RuntimeException|DriverException) { return; } diff --git a/src/Actions/ResizeImages.php b/src/Actions/ResizeImages.php index d689655..33346fb 100644 --- a/src/Actions/ResizeImages.php +++ b/src/Actions/ResizeImages.php @@ -16,10 +16,11 @@ class ResizeImages implements ResizesImages public function resize(bool $forceAll = false): Batch { /** @var AssetCollection $assets */ - $assets = AssetFacade::all(); + $assets = AssetFacade::all()->getOptimizableAssets(); // @phpstan-ignore-line - $assets->getOptimizableAssets() // @phpstan-ignore-line - ->when(! $forceAll, fn () => $assets->whereNull('image-optimized')); + if (! $forceAll) { + $assets = $assets->whereNull('image-optimized'); + } $jobs = $assets ->filter(fn (Asset $asset): bool => $asset->isImage()) diff --git a/src/Http/Controllers/CP/ImageResizeController.php b/src/Http/Controllers/CP/ImageResizeController.php index 2dd9914..27fd881 100644 --- a/src/Http/Controllers/CP/ImageResizeController.php +++ b/src/Http/Controllers/CP/ImageResizeController.php @@ -2,21 +2,22 @@ namespace JustBetter\ImageOptimize\Http\Controllers\CP; -use Illuminate\Contracts\View\Factory; -use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\DB; +use Inertia\Inertia; +use Inertia\Response; use JustBetter\ImageOptimize\Contracts\ResizesImages; use Statamic\Facades\Asset; +use Throwable; class ImageResizeController extends Controller { /** * @codeCoverageIgnore */ - public function index(): Factory|View|string + public function index(): Response { $assets = Asset::all()->getOptimizableAssets(); // @phpstan-ignore-line $unoptimizedAssets = $assets->whereNull('image-optimized'); @@ -24,46 +25,52 @@ public function index(): Factory|View|string try { DB::connection()->getPdo(); - } catch (\Exception $e) { + } catch (Throwable) { $databaseConnected = false; } - return view('statamic-image-optimize::cp.image-resize.index', [ - 'title' => 'JustBetter Image Optimize', - 'total_assets' => $assets->count(), - 'unoptimized_assets' => $unoptimizedAssets->count(), - 'can_optimize' => $databaseConnected, + return Inertia::render('statamic-image-optimize::ImageResize/Index', [ + 'totalAssets' => $assets->count(), + 'unoptimizedAssets' => $unoptimizedAssets->count(), + 'canOptimize' => $databaseConnected, + 'startBatchUrl' => cp_route('statamic-image-optimize.batches.start'), + 'batchStatusUrlTemplate' => cp_route('statamic-image-optimize.batches.status', ['batchId' => '__BATCH_ID__']), ]); } - public function resizeImages(ResizesImages $resizesImages, ?string $forceAll = null): JsonResponse + public function startBatch(ResizesImages $resizesImages): JsonResponse { - $batch = $resizesImages->resize($forceAll !== null); + $validated = request()->validate([ + 'scope' => ['required', 'string', 'in:remaining,all'], + ]); + + $batch = $resizesImages->resize($validated['scope'] === 'all'); return response()->json([ - 'imagesOptimized' => true, 'batchId' => $batch->id, ]); } - public function resizeImagesJobCount(?string $batchId = null): JsonResponse + public function batchStatus(string $batchId): JsonResponse { - $batch = $batchId ? Bus::findBatch($batchId) : null; + $batch = Bus::findBatch($batchId); if ($batch) { + $processedJobs = max(0, $batch->totalJobs - $batch->pendingJobs - $batch->failedJobs); + return response()->json([ - 'assetsToOptimize' => $batch->pendingJobs, - 'assetTotal' => $batch->totalJobs, + 'batchId' => $batch->id, + 'total' => $batch->totalJobs, + 'pending' => $batch->pendingJobs, + 'failed' => $batch->failedJobs, + 'processed' => $processedJobs, + 'finished' => $batch->finished(), ]); } - $allAssets = Asset::all(); - $assets = $allAssets->getOptimizableAssets() // @phpstan-ignore-line - ->whereNull('image-optimized'); - return response()->json([ - 'assetsToOptimize' => $assets->count(), - 'assetTotal' => $allAssets->count(), - ]); + 'batchId' => $batchId, + 'missing' => true, + ], 404); } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 26bf7b3..c731d5a 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -108,7 +108,7 @@ protected function bootNav(): static $nav->create('Image Optimize') ->section('Tools') ->route('statamic-image-optimize.index') - ->icon('collection'); + ->icon('insert-image'); }); return $this; diff --git a/tests/Actions/ResizeImageTest.php b/tests/Actions/ResizeImageTest.php index 7fc1270..b1efb72 100644 --- a/tests/Actions/ResizeImageTest.php +++ b/tests/Actions/ResizeImageTest.php @@ -3,12 +3,12 @@ namespace JustBetter\ImageOptimize\Tests\Actions; use Illuminate\Support\Facades\Event; -use Intervention\Image\Exception\NotReadableException; -use Intervention\Image\Facades\Image; +use Illuminate\Support\Facades\Storage; use JustBetter\ImageOptimize\Actions\ResizeImage; use JustBetter\ImageOptimize\Events\ImageResizedEvent; use JustBetter\ImageOptimize\Tests\TestCase; use PHPUnit\Framework\Attributes\Test; +use Statamic\Assets\Asset; class ResizeImageTest extends TestCase { @@ -53,11 +53,29 @@ public function it_can_catch_exceptions(): void { Event::fake(); - Image::spy() - ->shouldReceive('make') - ->andThrow(NotReadableException::class); + Storage::disk('assets')->put('broken.png', 'this-is-not-a-valid-image'); - $asset = $this->createAsset(); + $asset = new Asset; + $asset->container($this->assetContainer()); + $asset->path('broken.png'); + $asset->save(); + + /** @var ResizeImage $action */ + $action = app(ResizeImage::class); + $action->resize($asset); + + Event::assertNotDispatched(ImageResizedEvent::class); + } + + #[Test] + public function it_can_skip_missing_assets(): void + { + Event::fake(); + + $asset = new Asset; + $asset->container($this->assetContainer()); + $asset->path('does-not-exist.png'); + $asset->save(); /** @var ResizeImage $action */ $action = app(ResizeImage::class); diff --git a/tests/Actions/ResizeImagesTest.php b/tests/Actions/ResizeImagesTest.php index c1318b8..51d81f0 100644 --- a/tests/Actions/ResizeImagesTest.php +++ b/tests/Actions/ResizeImagesTest.php @@ -11,6 +11,8 @@ use JustBetter\ImageOptimize\Tests\TestCase; use Orchestra\Testbench\Attributes\WithMigration; use PHPUnit\Framework\Attributes\Test; +use Statamic\Assets\AssetCollection; +use Statamic\Facades\Asset; class ResizeImagesTest extends TestCase { @@ -19,7 +21,10 @@ public function it_can_resize_images(): void { Bus::fake(); - $this->createAsset(); + $asset = $this->createAsset(); + + Asset::shouldReceive('all') + ->andReturn(new AssetCollection([$asset])); /** @var ResizeImages $action */ $action = app(ResizeImages::class); diff --git a/tests/Http/Controllers/ImageResizeControllerTest.php b/tests/Http/Controllers/ImageResizeControllerTest.php index 26bf788..de72764 100644 --- a/tests/Http/Controllers/ImageResizeControllerTest.php +++ b/tests/Http/Controllers/ImageResizeControllerTest.php @@ -12,50 +12,51 @@ class ImageResizeControllerTest extends TestCase { #[Test] - public function it_can_resize_images(): void + public function it_can_start_a_batch_for_remaining_images(): void { $fakeBatch = new BatchFake('::batch-id::', '::name::', 0, 0, 0, [], [], now()->toImmutable()); $this->mock(ResizesImages::class, function (MockInterface $mock) use ($fakeBatch): void { $mock ->shouldReceive('resize') + ->with(false) ->andReturn($fakeBatch); }); $this ->withoutMiddleware() - ->get(route('statamic.cp.statamic-image-optimize.resize-images')) + ->post(route('statamic.cp.statamic-image-optimize.batches.start'), ['scope' => 'remaining']) ->assertSuccessful() ->assertJson([ - 'imagesOptimized' => true, 'batchId' => '::batch-id::', ]); } #[Test] - public function it_can_get_resize_images_count(): void + public function it_can_start_a_batch_for_all_images(): void { - Bus::fake(); + $fakeBatch = new BatchFake('::batch-id::', '::name::', 0, 0, 0, [], [], now()->toImmutable()); - $this->createAsset(); + $this->mock(ResizesImages::class, function (MockInterface $mock) use ($fakeBatch): void { + $mock + ->shouldReceive('resize') + ->with(true) + ->andReturn($fakeBatch); + }); $this ->withoutMiddleware() - ->get(route('statamic.cp.statamic-image-optimize.resize-images-count')) + ->post(route('statamic.cp.statamic-image-optimize.batches.start'), ['scope' => 'all']) ->assertSuccessful() ->assertJson([ - 'assetsToOptimize' => 1, - 'assetTotal' => 1, + 'batchId' => '::batch-id::', ]); } #[Test] - public function it_can_get_resize_images_count_with_batch(): void + public function it_can_get_batch_status(): void { - Bus::fake(); - - $this->createAsset(); - $fakeBatch = new BatchFake('::batch-id::', '::name::', 1, 0, 0, [], [], now()->toImmutable()); + $fakeBatch = new BatchFake('::batch-id::', '::name::', 2, 1, 1, [], [], now()->toImmutable()); Bus::spy() ->shouldReceive('findBatch') @@ -63,11 +64,31 @@ public function it_can_get_resize_images_count_with_batch(): void $this ->withoutMiddleware() - ->get(route('statamic.cp.statamic-image-optimize.resize-images-count', ['batchId' => '::batch-id::'])) + ->get(route('statamic.cp.statamic-image-optimize.batches.status', ['batchId' => '::batch-id::'])) ->assertSuccessful() ->assertJson([ - 'assetsToOptimize' => 0, - 'assetTotal' => 1, + 'batchId' => '::batch-id::', + 'total' => 2, + 'pending' => 1, + 'failed' => 1, + 'processed' => 0, + ]); + } + + #[Test] + public function it_returns_404_when_batch_is_missing(): void + { + Bus::spy() + ->shouldReceive('findBatch') + ->andReturn(null); + + $this + ->withoutMiddleware() + ->get(route('statamic.cp.statamic-image-optimize.batches.status', ['batchId' => '::missing::'])) + ->assertNotFound() + ->assertJson([ + 'batchId' => '::missing::', + 'missing' => true, ]); } } diff --git a/tests/Listeners/AssetUploadedListenerTest.php b/tests/Listeners/AssetUploadedListenerTest.php index 54c121f..36d2a92 100644 --- a/tests/Listeners/AssetUploadedListenerTest.php +++ b/tests/Listeners/AssetUploadedListenerTest.php @@ -18,7 +18,7 @@ public function it_can_resize_an_image(): void $asset = $this->createAsset(); - $event = new AssetUploaded($asset); + $event = new AssetUploaded($asset, 'test.png'); /** @var AssetUploadedListener $listener */ $listener = app(AssetUploadedListener::class); diff --git a/tests/TestCase.php b/tests/TestCase.php index 9b90ef9..49c7aae 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,7 +5,6 @@ use Illuminate\Foundation\Testing\Concerns\InteractsWithViews; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use Illuminate\Support\Facades\Storage; -use Intervention\Image\ImageServiceProvider; use JustBetter\ImageOptimize\ServiceProvider; use Statamic\Assets\Asset; use Statamic\Assets\AssetContainer; @@ -20,13 +19,11 @@ abstract class TestCase extends AddonTestCase protected string $addonServiceProvider = ServiceProvider::class; - protected ?AssetContainer $assetContainer = null; + protected AssetContainer $assetContainer; protected function getPackageProviders($app) { - return array_merge(parent::getPackageProviders($app), [ - ImageServiceProvider::class, - ]); + return parent::getPackageProviders($app); } protected function defineEnvironment($app): void @@ -52,11 +49,13 @@ protected function defineEnvironment($app): void protected function assetContainer(): AssetContainer { - if ($this->assetContainer === null) { - $this->assetContainer = (new AssetContainer) // @phpstan-ignore-line - ->handle('test_container') - ->disk('assets') - ->save(); + if (! isset($this->assetContainer)) { + $container = new AssetContainer; + $container->handle('test_container'); + $container->disk('assets'); + $container->save(); + + $this->assetContainer = $container; } return $this->assetContainer; @@ -75,11 +74,18 @@ protected function fixturePath(string $file = ''): string protected function createAsset(string $filename = 'test.png'): Asset { - Storage::disk('assets')->put($filename, file_get_contents($this->fixturePath('uploads/test.png'))); // @phpstan-ignore-line + $contents = file_get_contents($this->fixturePath('uploads/test.png')); + if ($contents === false) { + throw new \RuntimeException('Unable to read test fixture image.'); + } + + Storage::disk('assets')->put($filename, $contents); - /** @var Asset $asset */ - $asset = (new Asset)->container($this->assetContainer())->path($filename); // @phpstan-ignore-line + $asset = new Asset; + $asset->container($this->assetContainer()); + $asset->path($filename); $asset->save(); + $asset->meta(); return $asset; } diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..6004130 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import statamic from '@statamic/cms/vite-plugin'; + +export default defineConfig({ + plugins: [statamic()], + build: { + outDir: 'dist', + rollupOptions: { + input: 'resources/js/statamic-image-optimize.js', + output: { + entryFileNames: 'js/statamic-image-optimize.js', + chunkFileNames: 'js/[name]-[hash].js', + assetFileNames: '[name]-[hash][extname]', + }, + }, + }, +}); + diff --git a/webpack.mix.js b/webpack.mix.js deleted file mode 100644 index 76575cf..0000000 --- a/webpack.mix.js +++ /dev/null @@ -1,3 +0,0 @@ -let mix = require('laravel-mix'); - -mix.js('resources/js/statamic-image-optimize.js', 'dist/js/statamic-image-optimize.js').vue({ version: 2 }); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..e4b3a8e --- /dev/null +++ b/yarn.lock @@ -0,0 +1,550 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" + +"@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@esbuild/aix-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" + integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== + +"@esbuild/android-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" + integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== + +"@esbuild/android-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" + integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== + +"@esbuild/android-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" + integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== + +"@esbuild/darwin-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== + +"@esbuild/darwin-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" + integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== + +"@esbuild/freebsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" + integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== + +"@esbuild/freebsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" + integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== + +"@esbuild/linux-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" + integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== + +"@esbuild/linux-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" + integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== + +"@esbuild/linux-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" + integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== + +"@esbuild/linux-loong64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" + integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== + +"@esbuild/linux-mips64el@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" + integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== + +"@esbuild/linux-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" + integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== + +"@esbuild/linux-riscv64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" + integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== + +"@esbuild/linux-s390x@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" + integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== + +"@esbuild/linux-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== + +"@esbuild/netbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" + integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== + +"@esbuild/netbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" + integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== + +"@esbuild/openbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" + integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== + +"@esbuild/openbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" + integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== + +"@esbuild/openharmony-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" + integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== + +"@esbuild/sunos-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" + integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== + +"@esbuild/win32-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" + integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== + +"@esbuild/win32-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" + integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== + +"@esbuild/win32-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" + integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== + +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@rolldown/pluginutils@1.0.0-rc.2": + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz#10324e74cb3396cb7b616042ea7e9e6aa7d8d458" + integrity sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw== + +"@rollup/rollup-android-arm-eabi@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz#a6742c74c7d9d6d604ef8a48f99326b4ecda3d82" + integrity sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg== + +"@rollup/rollup-android-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz#97247be098de4df0c11971089fd2edf80a5da8cf" + integrity sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q== + +"@rollup/rollup-darwin-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz#674852cf14cf11b8056e0b1a2f4e872b523576cf" + integrity sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg== + +"@rollup/rollup-darwin-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz#36dfd7ed0aaf4d9d89d9ef983af72632455b0246" + integrity sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w== + +"@rollup/rollup-freebsd-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz#2f87c2074b4220260fdb52a9996246edfc633c22" + integrity sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA== + +"@rollup/rollup-freebsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz#9b5a26522a38a95dc06616d1939d4d9a76937803" + integrity sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg== + +"@rollup/rollup-linux-arm-gnueabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz#86aa4859385a8734235b5e40a48e52d770758c3a" + integrity sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw== + +"@rollup/rollup-linux-arm-musleabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz#cbe70e56e6ece8dac83eb773b624fc9e5a460976" + integrity sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA== + +"@rollup/rollup-linux-arm64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz#d14992a2e653bc3263d284bc6579b7a2890e1c45" + integrity sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA== + +"@rollup/rollup-linux-arm64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz#2fdd1ddc434ea90aeaa0851d2044789b4d07f6da" + integrity sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA== + +"@rollup/rollup-linux-loong64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz#8a181e6f89f969f21666a743cd411416c80099e7" + integrity sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg== + +"@rollup/rollup-linux-loong64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz#904125af2babc395f8061daa27b5af1f4e3f2f78" + integrity sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q== + +"@rollup/rollup-linux-ppc64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz#a57970ac6864c9a3447411a658224bdcf948be22" + integrity sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA== + +"@rollup/rollup-linux-ppc64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz#bb84de5b26870567a4267666e08891e80bb56a63" + integrity sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA== + +"@rollup/rollup-linux-riscv64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz#72d00d2c7fb375ce3564e759db33f17a35bffab9" + integrity sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg== + +"@rollup/rollup-linux-riscv64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz#4c166ef58e718f9245bd31873384ba15a5c1a883" + integrity sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg== + +"@rollup/rollup-linux-s390x-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz#bb5025cde9a61db478c2ca7215808ad3bce73a09" + integrity sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w== + +"@rollup/rollup-linux-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz#9b66b1f9cd95c6624c788f021c756269ffed1552" + integrity sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg== + +"@rollup/rollup-linux-x64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz#b007ca255dc7166017d57d7d2451963f0bd23fd9" + integrity sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg== + +"@rollup/rollup-openbsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz#e8b357b2d1aa2c8d76a98f5f0d889eabe93f4ef9" + integrity sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ== + +"@rollup/rollup-openharmony-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz#96c2e3f4aacd3d921981329831ff8dde492204dc" + integrity sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA== + +"@rollup/rollup-win32-arm64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz#2d865149d706d938df8b4b8f117e69a77646d581" + integrity sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A== + +"@rollup/rollup-win32-ia32-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz#abe1593be0fa92325e9971c8da429c5e05b92c36" + integrity sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA== + +"@rollup/rollup-win32-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz#c4af3e9518c9a5cd4b1c163dc81d0ad4d82e7eab" + integrity sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA== + +"@rollup/rollup-win32-x64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c" + integrity sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA== + +"@statamic/cms@file:./vendor/statamic/cms/resources/dist-package": + version "0.0.0" + dependencies: + "@vitejs/plugin-vue" "^6.0.0" + +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@vitejs/plugin-vue@^6.0.0": + version "6.0.5" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz#20ebb46c4da069753d9cfb1309c4334213cc3f7b" + integrity sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg== + dependencies: + "@rolldown/pluginutils" "1.0.0-rc.2" + +"@vue/compiler-core@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.30.tgz#0f984da9207f24f9ddfb700052a43247a953fd9a" + integrity sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw== + dependencies: + "@babel/parser" "^7.29.0" + "@vue/shared" "3.5.30" + entities "^7.0.1" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz#a38dbdd520479244c8b673123b4bd06a82e733ee" + integrity sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g== + dependencies: + "@vue/compiler-core" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/compiler-sfc@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz#5c716d844f240154263e99b25fba6e1802c0c8c6" + integrity sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A== + dependencies: + "@babel/parser" "^7.29.0" + "@vue/compiler-core" "3.5.30" + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" + estree-walker "^2.0.2" + magic-string "^0.30.21" + postcss "^8.5.8" + source-map-js "^1.2.1" + +"@vue/compiler-ssr@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz#e9b407d7e56be1e307a7621f2e8d2501267ff1d0" + integrity sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA== + dependencies: + "@vue/compiler-dom" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/reactivity@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.30.tgz#1ff13f7d570b16b4f009f007772c7b71be1dd09d" + integrity sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q== + dependencies: + "@vue/shared" "3.5.30" + +"@vue/runtime-core@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.30.tgz#abe448b25e88f583b1847323a2f19f5e4a21837d" + integrity sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg== + dependencies: + "@vue/reactivity" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/runtime-dom@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz#41d1b6424b754300f735c2ecb1a7457b4125dab3" + integrity sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw== + dependencies: + "@vue/reactivity" "3.5.30" + "@vue/runtime-core" "3.5.30" + "@vue/shared" "3.5.30" + csstype "^3.2.3" + +"@vue/server-renderer@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.30.tgz#116515063d609d3ceca1170f3b09122f24f187b5" + integrity sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ== + dependencies: + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/shared@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.30.tgz#5d7a0d3ca151647484303fd9f057e2e13ecb80ef" + integrity sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ== + +csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + +esbuild@^0.25.0: + version "0.25.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.12" + "@esbuild/android-arm" "0.25.12" + "@esbuild/android-arm64" "0.25.12" + "@esbuild/android-x64" "0.25.12" + "@esbuild/darwin-arm64" "0.25.12" + "@esbuild/darwin-x64" "0.25.12" + "@esbuild/freebsd-arm64" "0.25.12" + "@esbuild/freebsd-x64" "0.25.12" + "@esbuild/linux-arm" "0.25.12" + "@esbuild/linux-arm64" "0.25.12" + "@esbuild/linux-ia32" "0.25.12" + "@esbuild/linux-loong64" "0.25.12" + "@esbuild/linux-mips64el" "0.25.12" + "@esbuild/linux-ppc64" "0.25.12" + "@esbuild/linux-riscv64" "0.25.12" + "@esbuild/linux-s390x" "0.25.12" + "@esbuild/linux-x64" "0.25.12" + "@esbuild/netbsd-arm64" "0.25.12" + "@esbuild/netbsd-x64" "0.25.12" + "@esbuild/openbsd-arm64" "0.25.12" + "@esbuild/openbsd-x64" "0.25.12" + "@esbuild/openharmony-arm64" "0.25.12" + "@esbuild/sunos-x64" "0.25.12" + "@esbuild/win32-arm64" "0.25.12" + "@esbuild/win32-ia32" "0.25.12" + "@esbuild/win32-x64" "0.25.12" + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +fdir@^6.4.4, fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +postcss@^8.4.21, postcss@^8.5.3, postcss@^8.5.8: + version "8.5.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.8.tgz#6230ecc8fb02e7a0f6982e53990937857e13f399" + integrity sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +rollup@^4.34.9: + version "4.59.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.59.0.tgz#cf74edac17c1486f562d728a4d923a694abdf06f" + integrity sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.59.0" + "@rollup/rollup-android-arm64" "4.59.0" + "@rollup/rollup-darwin-arm64" "4.59.0" + "@rollup/rollup-darwin-x64" "4.59.0" + "@rollup/rollup-freebsd-arm64" "4.59.0" + "@rollup/rollup-freebsd-x64" "4.59.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.59.0" + "@rollup/rollup-linux-arm-musleabihf" "4.59.0" + "@rollup/rollup-linux-arm64-gnu" "4.59.0" + "@rollup/rollup-linux-arm64-musl" "4.59.0" + "@rollup/rollup-linux-loong64-gnu" "4.59.0" + "@rollup/rollup-linux-loong64-musl" "4.59.0" + "@rollup/rollup-linux-ppc64-gnu" "4.59.0" + "@rollup/rollup-linux-ppc64-musl" "4.59.0" + "@rollup/rollup-linux-riscv64-gnu" "4.59.0" + "@rollup/rollup-linux-riscv64-musl" "4.59.0" + "@rollup/rollup-linux-s390x-gnu" "4.59.0" + "@rollup/rollup-linux-x64-gnu" "4.59.0" + "@rollup/rollup-linux-x64-musl" "4.59.0" + "@rollup/rollup-openbsd-x64" "4.59.0" + "@rollup/rollup-openharmony-arm64" "4.59.0" + "@rollup/rollup-win32-arm64-msvc" "4.59.0" + "@rollup/rollup-win32-ia32-msvc" "4.59.0" + "@rollup/rollup-win32-x64-gnu" "4.59.0" + "@rollup/rollup-win32-x64-msvc" "4.59.0" + fsevents "~2.3.2" + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +tinyglobby@^0.2.13: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +vite@^6.0.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.1.tgz#afbe14518cdd6887e240a4b0221ab6d0ce733f96" + integrity sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g== + dependencies: + esbuild "^0.25.0" + fdir "^6.4.4" + picomatch "^4.0.2" + postcss "^8.5.3" + rollup "^4.34.9" + tinyglobby "^0.2.13" + optionalDependencies: + fsevents "~2.3.3" + +vue@^3.5.0: + version "3.5.30" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.30.tgz#66df25e9795af3e5522b36f24f3d290fde83f8a0" + integrity sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg== + dependencies: + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-sfc" "3.5.30" + "@vue/runtime-dom" "3.5.30" + "@vue/server-renderer" "3.5.30" + "@vue/shared" "3.5.30" From 49208c47f57e05b7e92639c08a94b08143c87962 Mon Sep 17 00:00:00 2001 From: Kevin Meijer Date: Wed, 18 Mar 2026 13:41:35 +0100 Subject: [PATCH 2/5] Updated Github workflows --- .github/workflows/analyse.yml | 6 +++--- .github/workflows/coverage.yml | 8 ++++---- .github/workflows/style.yml | 2 +- .github/workflows/tests.yml | 11 ++++------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml index 885b94d..3e9b35b 100644 --- a/.github/workflows/analyse.yml +++ b/.github/workflows/analyse.yml @@ -10,11 +10,11 @@ jobs: matrix: os: [ubuntu-latest] php: [8.3] - laravel: [11.*] + laravel: [12.*] stability: [prefer-stable] include: - - laravel: 11.* - testbench: 9.* + - laravel: 12.* + testbench: 10.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 18d1fa0..07d5231 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,12 +9,12 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.3] - laravel: [11.*] + php: [8.4, 8.5] + laravel: [12.*] stability: [prefer-stable] include: - - laravel: 11.* - testbench: 9.* + - laravel: 12.* + testbench: 10.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index a61cfdc..e320ec5 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -16,7 +16,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.4, 8.5 extensions: dom, curl, libxml, mbstring, zip, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo coverage: none diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bac831d..84912eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,15 +9,12 @@ jobs: fail-fast: true matrix: os: [ ubuntu-latest ] - php: [ 8.2, 8.3 ] - laravel: [ 11.* ] + php: [ 8.4, 8.5 ] + laravel: [ 12.* ] stability: [ prefer-lowest, prefer-stable ] include: - - laravel: 11.* - testbench: 9.* - exclude: - - laravel: 11.* - php: 8.1 + - laravel: 12.* + testbench: 10.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} From 622fc131dc5724d03e0a2d7ce3f7be922145611e Mon Sep 17 00:00:00 2001 From: Kevin Meijer Date: Wed, 18 Mar 2026 14:35:09 +0100 Subject: [PATCH 3/5] Refactored image resizing based on ratio --- config/image-optimize.php | 8 ++--- src/Actions/ResizeImage.php | 32 ++++++++++++++--- src/Contracts/ResizesImage.php | 2 +- src/Jobs/ResizeImageJob.php | 4 +-- tests/Actions/ResizeImageTest.php | 59 +++++++++++++++++++++++++++---- tests/Jobs/ResizeImageJobTest.php | 2 +- 6 files changed, 87 insertions(+), 20 deletions(-) diff --git a/config/image-optimize.php b/config/image-optimize.php index b274569..13b9e08 100644 --- a/config/image-optimize.php +++ b/config/image-optimize.php @@ -1,11 +1,11 @@ env('IMAGE_OPTIMIZE_WIDTH', 1600), + // Set the max resize width in pixels + 'max_resize_width' => env('IMAGE_OPTIMIZE_WIDTH', 2560), - // Set the default resize height in pixels - 'default_resize_height' => env('IMAGE_OPTIMIZE_HEIGHT', 1600), + // Set the max resize height in pixels + 'max_resize_height' => env('IMAGE_OPTIMIZE_HEIGHT', 2560), // Set the default queue name 'default_queue_name' => env('IMAGE_OPTIMIZE_QUEUE_NAME', 'default'), diff --git a/src/Actions/ResizeImage.php b/src/Actions/ResizeImage.php index 248ac28..8befc61 100644 --- a/src/Actions/ResizeImage.php +++ b/src/Actions/ResizeImage.php @@ -11,7 +11,7 @@ class ResizeImage implements ResizesImage { - public function resize(Asset $asset, ?int $width = null, ?int $height = null): void + public function resize(Asset $asset): void { if (! $asset->exists() || ! $asset->isImage() || @@ -20,14 +20,36 @@ public function resize(Asset $asset, ?int $width = null, ?int $height = null): v return; } - $width ??= (int) config('image-optimize.default_resize_width'); - $height ??= (int) config('image-optimize.default_resize_height'); + $maxWidth = (int) config('image-optimize.max_resize_width'); + $maxHeight = (int) config('image-optimize.max_resize_height'); try { $manager = ImageManager::gd(); - $image = $manager->read($asset->resolvedPath()) - ->scaleDown($width, $height); + $image = $manager->read($asset->resolvedPath()); + + $originalWidth = $image->width(); + $originalHeight = $image->height(); + + // @codeCoverageIgnoreStart + if ($originalWidth <= 0 || $originalHeight <= 0) { + return; + } + // @codeCoverageIgnoreEnd + + $maxWidth = $maxWidth > 0 ? $maxWidth : $originalWidth; + $maxHeight = $maxHeight > 0 ? $maxHeight : $originalHeight; + + $widthScale = $maxWidth / $originalWidth; + $heightScale = $maxHeight / $originalHeight; + + if ($originalWidth > $maxWidth || $originalHeight > $maxHeight) { + if ($widthScale <= $heightScale) { + $image = $image->scaleDown($maxWidth, null); + } else { + $image = $image->scaleDown(null, $maxHeight); + } + } $extension = $asset->extension(); diff --git a/src/Contracts/ResizesImage.php b/src/Contracts/ResizesImage.php index 0aabc2d..7f251cb 100644 --- a/src/Contracts/ResizesImage.php +++ b/src/Contracts/ResizesImage.php @@ -6,5 +6,5 @@ interface ResizesImage { - public function resize(Asset $asset, ?int $width = null, ?int $height = null): void; + public function resize(Asset $asset): void; } diff --git a/src/Jobs/ResizeImageJob.php b/src/Jobs/ResizeImageJob.php index 3fedfe8..7667ab2 100644 --- a/src/Jobs/ResizeImageJob.php +++ b/src/Jobs/ResizeImageJob.php @@ -20,8 +20,6 @@ class ResizeImageJob implements ShouldBeUnique, ShouldQueue public function __construct( public Asset $asset, - public ?int $width = null, - public ?int $height = null, ) { $this->onConnection(config('image-optimize.default_queue_connection')); $this->onQueue(config('image-optimize.default_queue_name')); @@ -29,7 +27,7 @@ public function __construct( public function handle(ResizesImage $contract): void { - $contract->resize($this->asset, $this->width, $this->height); + $contract->resize($this->asset); } public function uniqueId(): string diff --git a/tests/Actions/ResizeImageTest.php b/tests/Actions/ResizeImageTest.php index b1efb72..544e989 100644 --- a/tests/Actions/ResizeImageTest.php +++ b/tests/Actions/ResizeImageTest.php @@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Storage; +use Intervention\Image\ImageManager; use JustBetter\ImageOptimize\Actions\ResizeImage; use JustBetter\ImageOptimize\Events\ImageResizedEvent; use JustBetter\ImageOptimize\Tests\TestCase; @@ -13,19 +14,65 @@ class ResizeImageTest extends TestCase { #[Test] - public function it_can_resize_image(): void + public function it_resizes_within_max_dimensions_without_changing_aspect_ratio(): void { Event::fake(); - $asset = $this->createAsset(); + config()->set('image-optimize.max_resize_width', 2560); + config()->set('image-optimize.max_resize_height', 2560); + + $manager = ImageManager::gd(); + $contents = $manager->create(6000, 4000)->encodeByExtension('png')->toString(); + + /** @var Asset $asset */ + $asset = $this->createAsset('landscape.png'); + $asset->disk()->filesystem()->put($asset->path(), $contents); + $asset->meta(); /** @var ResizeImage $action */ $action = app(ResizeImage::class); - $action->resize($asset, 100, 100); + $action->resize($asset); + + $resized = $manager->read($asset->resolvedPath()); + + $this->assertSame(2560, $resized->width()); + $this->assertSame(1707, $resized->height()); + + $this->assertSame(2560, $asset->meta('width')); + $this->assertSame(1707, $asset->meta('height')); + $this->assertSame(1, (int) $asset->meta('data.image-optimized')); + + Event::assertDispatched(ImageResizedEvent::class); + } + + #[Test] + public function it_resizes_portrait_images_within_max_dimensions_without_changing_aspect_ratio(): void + { + Event::fake(); + + config()->set('image-optimize.max_resize_width', 2560); + config()->set('image-optimize.max_resize_height', 2560); + + $manager = ImageManager::gd(); + $contents = $manager->create(4000, 6000)->encodeByExtension('png')->toString(); + + /** @var Asset $asset */ + $asset = $this->createAsset('portrait.png'); + $asset->disk()->filesystem()->put($asset->path(), $contents); + $asset->meta(); + + /** @var ResizeImage $action */ + $action = app(ResizeImage::class); + $action->resize($asset); + + $resized = $manager->read($asset->resolvedPath()); + + $this->assertSame(1707, $resized->width()); + $this->assertSame(2560, $resized->height()); - $this->assertEquals(100, $asset->meta('width')); - $this->assertEquals(63, $asset->meta('height')); - $this->assertEquals(1, $asset->meta('data.image-optimized')); + $this->assertSame(1707, $asset->meta('width')); + $this->assertSame(2560, $asset->meta('height')); + $this->assertSame(1, (int) $asset->meta('data.image-optimized')); Event::assertDispatched(ImageResizedEvent::class); } diff --git a/tests/Jobs/ResizeImageJobTest.php b/tests/Jobs/ResizeImageJobTest.php index 5cbce9f..e7a423c 100644 --- a/tests/Jobs/ResizeImageJobTest.php +++ b/tests/Jobs/ResizeImageJobTest.php @@ -21,6 +21,6 @@ public function it_can_resize_an_image(): void ->once(); }); - ResizeImageJob::dispatch($asset, 100, 100); + ResizeImageJob::dispatch($asset); } } From fc25d646d14729335b90638487e9d417156014cb Mon Sep 17 00:00:00 2001 From: Kevin Meijer Date: Wed, 18 Mar 2026 14:39:40 +0100 Subject: [PATCH 4/5] Updated workflow --- .github/workflows/analyse.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml index 3e9b35b..6bb7449 100644 --- a/.github/workflows/analyse.yml +++ b/.github/workflows/analyse.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.3] + php: [8.4, 8.5] laravel: [12.*] stability: [prefer-stable] include: From 636890f1287d464e59e69b09893525960c3d9e86 Mon Sep 17 00:00:00 2001 From: Kevin Meijer Date: Fri, 20 Mar 2026 09:38:30 +0100 Subject: [PATCH 5/5] Set driver based on config --- src/Actions/ResizeImage.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Actions/ResizeImage.php b/src/Actions/ResizeImage.php index 8befc61..5ef4b9a 100644 --- a/src/Actions/ResizeImage.php +++ b/src/Actions/ResizeImage.php @@ -24,7 +24,12 @@ public function resize(Asset $asset): void $maxHeight = (int) config('image-optimize.max_resize_height'); try { - $manager = ImageManager::gd(); + $driver = config('statamic.assets.image_manipulation.driver', 'gd'); + + $manager = match ($driver) { + 'imagick' => ImageManager::imagick(), + default => ImageManager::gd(), + }; $image = $manager->read($asset->resolvedPath());