build(ui): migrate from CRA/Jest to Vite/Vitest (#3311)

* feat: create vite project

* feat: it's alive!

* feat: `make dev` working!

* feat: replace custom serviceWorker with vite plugin

* test: replace Jest with Vitest

* fix: run prettier

* fix: skip eslint for now.

* chore: remove ui.old folder

* refactor: replace lodash.pick with simple destructuring

* fix: eslint errors (wip)

* fix: eslint errors (wip)

* fix: display-name eslint errors (wip)

* fix: no-console eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: build

* fix: pwa manifest

* refactor: pwa manifest

* refactor: simplify PORT configuration

* refactor: rename simple JS files

* test: cover playlistUtils

* fix: react-image-lightbox

* feat(ui): add sourcemaps to help debug issues
This commit is contained in:
Deluan Quintão
2024-09-28 11:54:36 -04:00
committed by GitHub
parent dd48a23f92
commit fcdd30ba8f
212 changed files with 6231 additions and 31060 deletions
-2
View File
@@ -1,2 +0,0 @@
this.workbox=this.workbox||{},this.workbox.core=function(t){"use strict";try{self["workbox:core:6.2.4"]&&_()}catch(t){}const e=(t,...e)=>{let n=t;return e.length>0&&(n+=" :: "+JSON.stringify(e)),n};class n extends Error{constructor(t,n){super(e(t,n)),this.name=t,this.details=n}}const r=new Set;const o={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},s=t=>[o.prefix,t,o.suffix].filter((t=>t&&t.length>0)).join("-"),i={updateDetails:t=>{(t=>{for(const e of Object.keys(o))t(e)})((e=>{"string"==typeof t[e]&&(o[e]=t[e])}))},getGoogleAnalyticsName:t=>t||s(o.googleAnalytics),getPrecacheName:t=>t||s(o.precache),getPrefix:()=>o.prefix,getRuntimeName:t=>t||s(o.runtime),getSuffix:()=>o.suffix};function c(t,e){const n=new URL(t);for(const t of e)n.searchParams.delete(t);return n.href}let a,u;function f(){if(void 0===u){const t=new Response("");if("body"in t)try{new Response(t.body),u=!0}catch(t){u=!1}u=!1}return u}function l(t){return new Promise((e=>setTimeout(e,t)))}var g=Object.freeze({__proto__:null,assert:null,cacheMatchIgnoreParams:async function(t,e,n,r){const o=c(e.url,n);if(e.url===o)return t.match(e,r);const s=Object.assign(Object.assign({},r),{ignoreSearch:!0}),i=await t.keys(e,s);for(const e of i){if(o===c(e.url,n))return t.match(e,r)}},cacheNames:i,canConstructReadableStream:function(){if(void 0===a)try{new ReadableStream({start(){}}),a=!0}catch(t){a=!1}return a},canConstructResponseFromBodyStream:f,dontWaitFor:function(t){t.then((()=>{}))},Deferred:class{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}},executeQuotaErrorCallbacks:async function(){for(const t of r)await t()},getFriendlyURL:t=>new URL(String(t),location.href).href.replace(new RegExp("^"+location.origin),""),logger:null,resultingClientExists:async function(t){if(!t)return;let e=await self.clients.matchAll({type:"window"});const n=new Set(e.map((t=>t.id)));let r;const o=performance.now();for(;performance.now()-o<2e3&&(e=await self.clients.matchAll({type:"window"}),r=e.find((e=>t?e.id===t:!n.has(e.id))),!r);)await l(100);return r},timeout:l,waitUntil:function(t,e){const n=e();return t.waitUntil(n),n},WorkboxError:n});const w={get googleAnalytics(){return i.getGoogleAnalyticsName()},get precache(){return i.getPrecacheName()},get prefix(){return i.getPrefix()},get runtime(){return i.getRuntimeName()},get suffix(){return i.getSuffix()}};return t._private=g,t.cacheNames=w,t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.copyResponse=async function(t,e){let r=null;if(t.url){r=new URL(t.url).origin}if(r!==self.location.origin)throw new n("cross-origin-copy-response",{origin:r});const o=t.clone(),s={headers:new Headers(o.headers),status:o.status,statusText:o.statusText},i=e?e(s):s,c=f()?o.body:await o.blob();return new Response(c,i)},t.registerQuotaErrorCallback=function(t){r.add(t)},t.setCacheNameDetails=function(t){i.updateDetails(t)},t.skipWaiting=function(){self.skipWaiting()},t}({});
//# sourceMappingURL=workbox-core.prod.js.map
@@ -1,2 +0,0 @@
this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self["workbox:navigation-preload:6.2.4"]&&_()}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",(t=>{t.waitUntil(self.registration.navigationPreload.disable().then((()=>{})))}))},t.enable=function(t){e()&&self.addEventListener("activate",(e=>{e.waitUntil(self.registration.navigationPreload.enable().then((()=>{t&&self.registration.navigationPreload.setHeaderValue(t)})))}))},t.isSupported=e,t}({});
//# sourceMappingURL=workbox-navigation-preload.prod.js.map
-2
View File
@@ -1,2 +0,0 @@
this.workbox=this.workbox||{},this.workbox.routing=function(t,e){"use strict";try{self["workbox:routing:6.2.4"]&&_()}catch(t){}const s=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,r="GET"){this.handler=s(e),this.match=t,this.method=r}setCatchHandler(t){this.catchHandler=s(t)}}class n extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class i{constructor(){this.ut=new Map,this.ft=new Map}get routes(){return this.ut}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const r=s.origin===location.origin,{params:n,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:r,url:s});let o=i&&i.handler;const u=t.method;if(!o&&this.ft.has(u)&&(o=this.ft.get(u)),!o)return;let c;try{c=o.handle({url:s,request:t,event:e,params:n})}catch(t){c=Promise.reject(t)}const a=i&&i.catchHandler;return c instanceof Promise&&(this.lt||a)&&(c=c.catch((async r=>{if(a)try{return await a.handle({url:s,request:t,event:e,params:n})}catch(t){t instanceof Error&&(r=t)}if(this.lt)return this.lt.handle({url:s,request:t,event:e});throw r}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:r}){const n=this.ut.get(s.method)||[];for(const i of n){let n;const o=i.match({url:t,sameOrigin:e,request:s,event:r});if(o)return n=o,(Array.isArray(n)&&0===n.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(n=void 0),{route:i,params:n}}return{}}setDefaultHandler(t,e="GET"){this.ft.set(e,s(t))}setCatchHandler(t){this.lt=s(t)}registerRoute(t){this.ut.has(t.method)||this.ut.set(t.method,[]),this.ut.get(t.method).push(t)}unregisterRoute(t){if(!this.ut.has(t.method))throw new e.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const s=this.ut.get(t.method).indexOf(t);if(!(s>-1))throw new e.WorkboxError("unregister-route-route-not-registered");this.ut.get(t.method).splice(s,1)}}let o;const u=()=>(o||(o=new i,o.addFetchListener(),o.addCacheListener()),o);return t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super((t=>this.dt(t)),t),this.wt=e,this.gt=s}dt({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.gt)if(t.test(s))return!1;return!!this.wt.some((t=>t.test(s)))}},t.RegExpRoute=n,t.Route=r,t.Router=i,t.registerRoute=function(t,s,i){let o;if("string"==typeof t){const e=new URL(t,location.href);o=new r((({url:t})=>t.href===e.href),s,i)}else if(t instanceof RegExp)o=new n(t,s,i);else if("function"==typeof t)o=new r(t,s,i);else{if(!(t instanceof r))throw new e.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return u().registerRoute(o),o},t.setCatchHandler=function(t){u().setCatchHandler(t)},t.setDefaultHandler=function(t){u().setDefaultHandler(t)},t}({},workbox.core._private);
//# sourceMappingURL=workbox-routing.prod.js.map
File diff suppressed because one or more lines are too long
-2
View File
@@ -1,2 +0,0 @@
!function(){"use strict";try{self["workbox:sw:6.3.0"]&&_()}catch(t){}const t={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams",recipes:"recipes"};self.workbox=new class{constructor(){return this.v={},this.Pt={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.$t=this.Pt.debug?"dev":"prod",this.Ct=!1,new Proxy(this,{get(e,s){if(e[s])return e[s];const o=t[s];return o&&e.loadModule("workbox-"+o),e[s]}})}setConfig(t={}){if(this.Ct)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.Pt,t),this.$t=this.Pt.debug?"dev":"prod"}loadModule(t){const e=this.jt(t);try{importScripts(e),this.Ct=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}jt(t){if(this.Pt.modulePathCb)return this.Pt.modulePathCb(t,this.Pt.debug);let e=["https://storage.googleapis.com/workbox-cdn/releases/6.3.0"];const s=`${t}.${this.$t}.js`,o=this.Pt.modulePathPrefix;return o&&(e=o.split("/"),""===e[e.length-1]&&e.splice(e.length-1,1)),e.push(s),e.join("/")}}}();
//# sourceMappingURL=workbox-sw.js.map
-60
View File
@@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="description"
content="Navidrome Music Server - {{.Version}}"
/>
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="192x192" href="%PUBLIC_URL%/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png">
<link rel="mask-icon" href="%PUBLIC_URL%/safari-pinned-tab.svg" color="#5b5fd5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--
manifest.webmanifest provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.webmanifest" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<meta property="og:site_name" content="Navidrome">
<meta property="og:url" content="{{ .ShareURL }}">
<meta property="og:title" content="{{ .ShareDescription }}">
<meta property="og:image" content="{{ .ShareImageURL }}">
<meta property="og:image:width" content="300">
<meta property="og:image:height" content="300">
<title>Navidrome</title>
<script>
window.__APP_CONFIG__ = {{ .AppConfig }}
</script>
<script>
window.__SHARE_INFO__ = {{ .ShareInfo }}
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
-22
View File
@@ -1,22 +0,0 @@
{
"name": "Navidrome",
"short_name": "Navidrome",
"description": "Navidrome, an open source web-based music collection server and streamer",
"categories": ["music", "entertainment"],
"display": "standalone",
"start_url": "./",
"background_color": "white",
"theme_color": "blue",
"icons": [
{
"src": "./android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
-53
View File
@@ -1,53 +0,0 @@
// documentation: https://developers.google.com/web/tools/workbox/modules/workbox-sw
importScripts('3rdparty/workbox/workbox-sw.js')
workbox.setConfig({
modulePathPrefix: '3rdparty/workbox/',
debug: false,
})
workbox.loadModule('workbox-core');
workbox.loadModule('workbox-strategies');
workbox.loadModule('workbox-routing');
workbox.loadModule('workbox-navigation-preload');
workbox.core.clientsClaim();
self.skipWaiting();
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
const CACHE_NAME = 'offline-html';
// This assumes /offline.html is a URL for your self-contained
// (no external images or styles) offline page.
const FALLBACK_HTML_URL = './offline.html';
// Populate the cache with the offline HTML page when the
// service worker is installed.
self.addEventListener('install', async (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.add(FALLBACK_HTML_URL))
);
});
const networkOnly = new workbox.strategies.NetworkOnly();
const navigationHandler = async (params) => {
try {
// Attempt a network request.
return await networkOnly.handle(params);
} catch (error) {
// If it fails, return the cached HTML.
return caches.match(FALLBACK_HTML_URL, {
cacheName: CACHE_NAME,
});
}
};
// Register this strategy to handle all navigations.
workbox.routing.registerRoute(
new workbox.routing.NavigationRoute(navigationHandler)
);
-10
View File
@@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head><title>Navidrome</title></head>
<body style="margin:0">
<p id="errorMessageDescription" style="text-align:center;font-size:21px;font-family:arial;margin-top:28px">
It looks like we are having trouble connecting.
<br/>
Please check your internet connection and try again.</p>
</body>
</html>