fix(player): support browsers without import maps

This commit is contained in:
Lukáš Lipinský
2026-08-29 17:09:36 +02:00
committed by Tobias Gesellchen
parent 35724bcffc
commit c97e9958f6
18 changed files with 114 additions and 51 deletions
@@ -5,15 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AfterTouch</title>
<meta name="description" content="AfterTouch — A replacement for Bose SoundTouch cloud services. Control your speakers after the cloud shutdown." />
<script type="importmap">
{
"imports": {
"preact": "/app/static/lib/preact.module.js",
"preact/hooks": "/app/static/lib/preact-hooks.module.js",
"htm": "/app/static/lib/htm.module.js"
}
}
</script>
<link rel="icon" type="image/svg+xml" href="/app/static/img/favicon.svg" />
<link rel="alternate icon" href="/app/static/img/favicon.ico" />
<link rel="stylesheet" href="/app/static/css/app.css" />
+3 -3
View File
@@ -1,6 +1,6 @@
import { h, render } from 'preact';
import { useState, useEffect, useCallback } from 'preact/hooks';
import htm from 'htm';
import { h, render } from '/app/static/lib/preact.module.js';
import { useState, useEffect, useCallback } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { DeviceList } from './components/DeviceList.js';
import { NowPlaying } from './components/NowPlaying.js';
import { Controls } from './components/Controls.js';
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect, useRef } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,5 +1,5 @@
import { h } from 'preact';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import htm from 'htm';
import { h } from '/app/static/lib/preact.module.js';
import { useState, useEffect } from '/app/static/lib/preact-hooks.module.js';
import htm from '/app/static/lib/htm.module.js';
import { api } from '../api.js';
const html = htm.bind(h);
@@ -1,2 +1,2 @@
import{options as n}from"preact";var t,r,u,i,o=0,f=[],c=n,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,p=c.__;function s(n,t){c.__h&&c.__h(r,n,o||t),o=0;var u=r.__H||(r.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function d(n){return o=1,y(D,n)}function y(n,u,i){var o=s(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}))}],o.__c=r,!r.__f)){var f=function(n,t,r){if(!o.__c.__H)return!0;var u=!1,i=o.__c.props!==n;if(o.__c.__H.__.some(function(n){if(n.__N){u=!0;var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0)}}),c){var f=c.call(this,n,t,r);return u?f||i:f}return!u||i};r.__f=!0;var c=r.shouldComponentUpdate,e=r.componentWillUpdate;r.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u}e&&e.call(this,n,t,r)},r.shouldComponentUpdate=f}return o.__N||o.__}function h(n,u){var i=s(t++,3);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__H.__h.push(i))}function _(n,u){var i=s(t++,4);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__h.push(i))}function A(n){return o=5,T(function(){return{current:n}},[])}function F(n,t,r){o=6,_(function(){if("function"==typeof n){var r=n(t());return function(){n(null),r&&"function"==typeof r&&r()}}if(n)return n.current=t(),function(){return n.current=null}},null==r?r:r.concat(n))}function T(n,r){var u=s(t++,7);return C(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q(n,t){return o=8,T(function(){return n},t)}function x(n){var u=r.context[n.__c],i=s(t++,9);return i.c=n,u?(null==i.__&&(i.__=!0,u.sub(r)),u.props.value):n.__}function P(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n)}function b(n){var u=s(t++,10),i=d();return u.__=n,r.componentDidCatch||(r.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n)}),[i[0],function(){i[1](void 0)}]}function g(){var n=s(t++,11);if(!n.__){for(var u=r.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++}return n.__}function j(){for(var n;n=f.shift();){var t=n.__H;if(n.__P&&t)try{t.__h.some(z),t.__h.some(B),t.__h=[]}catch(r){t.__h=[],c.__e(r,n.__v)}}}c.__b=function(n){r=null,e&&e(n)},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),p&&p(n,t)},c.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(i.__h.some(z),i.__h.some(B),i.__h=[],t=0)),u=r},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w)(j)),t.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0)})),u=r=null},c.__c=function(n,t){t.some(function(n){try{n.__h.some(z),n.__h=n.__h.filter(function(n){return!n.__||B(n)})}catch(r){t.some(function(n){n.__h&&(n.__h=[])}),t=[],c.__e(r,n.__v)}}),l&&l(n,t)},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.some(function(n){try{z(n)}catch(n){t=n}}),r.__H=void 0,t&&c.__e(t,r.__v))};var k="function"==typeof requestAnimationFrame;function w(n){var t,r=function(){clearTimeout(u),k&&cancelAnimationFrame(t),setTimeout(n)},u=setTimeout(r,35);k&&(t=requestAnimationFrame(r))}function z(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t}function B(n){var t=r;n.__c=n.__(),r=t}function C(n,t){return!n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D(n,t){return"function"==typeof t?t(n):t}export{q as useCallback,x as useContext,P as useDebugValue,h as useEffect,b as useErrorBoundary,g as useId,F as useImperativeHandle,_ as useLayoutEffect,T as useMemo,y as useReducer,A as useRef,d as useState};
import{options as n}from"./preact.module.js";var t,r,u,i,o=0,f=[],c=n,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,p=c.__;function s(n,t){c.__h&&c.__h(r,n,o||t),o=0;var u=r.__H||(r.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function d(n){return o=1,y(D,n)}function y(n,u,i){var o=s(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}))}],o.__c=r,!r.__f)){var f=function(n,t,r){if(!o.__c.__H)return!0;var u=!1,i=o.__c.props!==n;if(o.__c.__H.__.some(function(n){if(n.__N){u=!0;var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0)}}),c){var f=c.call(this,n,t,r);return u?f||i:f}return!u||i};r.__f=!0;var c=r.shouldComponentUpdate,e=r.componentWillUpdate;r.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u}e&&e.call(this,n,t,r)},r.shouldComponentUpdate=f}return o.__N||o.__}function h(n,u){var i=s(t++,3);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__H.__h.push(i))}function _(n,u){var i=s(t++,4);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__h.push(i))}function A(n){return o=5,T(function(){return{current:n}},[])}function F(n,t,r){o=6,_(function(){if("function"==typeof n){var r=n(t());return function(){n(null),r&&"function"==typeof r&&r()}}if(n)return n.current=t(),function(){return n.current=null}},null==r?r:r.concat(n))}function T(n,r){var u=s(t++,7);return C(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q(n,t){return o=8,T(function(){return n},t)}function x(n){var u=r.context[n.__c],i=s(t++,9);return i.c=n,u?(null==i.__&&(i.__=!0,u.sub(r)),u.props.value):n.__}function P(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n)}function b(n){var u=s(t++,10),i=d();return u.__=n,r.componentDidCatch||(r.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n)}),[i[0],function(){i[1](void 0)}]}function g(){var n=s(t++,11);if(!n.__){for(var u=r.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++}return n.__}function j(){for(var n;n=f.shift();){var t=n.__H;if(n.__P&&t)try{t.__h.some(z),t.__h.some(B),t.__h=[]}catch(r){t.__h=[],c.__e(r,n.__v)}}}c.__b=function(n){r=null,e&&e(n)},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),p&&p(n,t)},c.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(i.__h.some(z),i.__h.some(B),i.__h=[],t=0)),u=r},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w)(j)),t.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0)})),u=r=null},c.__c=function(n,t){t.some(function(n){try{n.__h.some(z),n.__h=n.__h.filter(function(n){return!n.__||B(n)})}catch(r){t.some(function(n){n.__h&&(n.__h=[])}),t=[],c.__e(r,n.__v)}}),l&&l(n,t)},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.some(function(n){try{z(n)}catch(n){t=n}}),r.__H=void 0,t&&c.__e(t,r.__v))};var k="function"==typeof requestAnimationFrame;function w(n){var t,r=function(){clearTimeout(u),k&&cancelAnimationFrame(t),setTimeout(n)},u=setTimeout(r,35);k&&(t=requestAnimationFrame(r))}function z(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t}function B(n){var t=r;n.__c=n.__(),r=t}function C(n,t){return!n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D(n,t){return"function"==typeof t?t(n):t}export{q as useCallback,x as useContext,P as useDebugValue,h as useEffect,b as useErrorBoundary,g as useId,F as useImperativeHandle,_ as useLayoutEffect,T as useMemo,y as useReducer,A as useRef,d as useState};
//# sourceMappingURL=hooks.module.js.map
@@ -0,0 +1,65 @@
package soundtouchweb
import (
"bytes"
"io/fs"
"regexp"
"strings"
"testing"
)
func TestStaticModulesDoNotRequireImportMaps(t *testing.T) {
index, err := fs.ReadFile(StaticFS, "static/index.html")
if err != nil {
t.Fatalf("read index: %v", err)
}
if bytes.Contains(index, []byte(`type="importmap"`)) {
t.Fatal("index.html must not require import map support")
}
dependencies := map[string]string{
"/app/static/lib/preact.module.js": "static/lib/preact.module.js",
"/app/static/lib/preact-hooks.module.js": "static/lib/preact-hooks.module.js",
"/app/static/lib/htm.module.js": "static/lib/htm.module.js",
}
usedDependencies := make(map[string]bool, len(dependencies))
for publicPath, embeddedPath := range dependencies {
if _, err := fs.Stat(StaticFS, embeddedPath); err != nil {
t.Errorf("static dependency %q: %v", publicPath, err)
}
}
bareDependency := regexp.MustCompile(`\bfrom\s*['"](?:preact(?:/hooks)?|htm)['"]`)
err = fs.WalkDir(StaticFS, "static", func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if entry.IsDir() || !strings.HasSuffix(path, ".js") {
return nil
}
source, err := fs.ReadFile(StaticFS, path)
if err != nil {
return err
}
if bareDependency.Match(source) {
t.Errorf("%s contains a bare dependency import", path)
}
for publicPath := range dependencies {
if bytes.Contains(source, []byte(publicPath)) {
usedDependencies[publicPath] = true
}
}
return nil
})
if err != nil {
t.Fatalf("walk JavaScript modules: %v", err)
}
for publicPath := range dependencies {
if !usedDependencies[publicPath] {
t.Errorf("static dependency %q is not imported", publicPath)
}
}
}
+7
View File
@@ -24,6 +24,13 @@ cp node_modules/preact/dist/preact.module.js "$LIB_DIR/preact.module.js"
echo "Copying Preact Hooks..."
cp node_modules/preact/hooks/dist/hooks.module.js "$LIB_DIR/preact-hooks.module.js"
# Browsers without import map support need the peer dependency to use a local URL.
sed -i.bak 's/from"preact"/from".\/preact.module.js"/' "$LIB_DIR/preact-hooks.module.js"
rm -f "$LIB_DIR/preact-hooks.module.js.bak"
if ! grep -q 'from".\/preact.module.js"' "$LIB_DIR/preact-hooks.module.js"; then
echo "Failed to rewrite the Preact Hooks peer dependency" >&2
exit 1
fi
echo "Copying HTM..."
cp node_modules/htm/dist/htm.module.js "$LIB_DIR/htm.module.js"