Compare commits
5
Commits
b5d0c484c0
...
d338529afc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d338529afc | ||
|
|
e3099bac5f | ||
|
|
322a59b31c | ||
|
|
e96dd0f06d | ||
|
|
f10d784a5c |
No files matched your search
+4
-2
@@ -38,7 +38,6 @@ $rsyncOptions = [
|
||||
'var',
|
||||
'.editorconfig',
|
||||
'.gitignore',
|
||||
'.htaccess_prod',
|
||||
'deploy.php',
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
@@ -52,7 +51,10 @@ $rsyncOptions = [
|
||||
'filter' => [],
|
||||
'filter-file' => false,
|
||||
'filter-perdir' => false,
|
||||
'flags' => 'rz',
|
||||
// -t preserves mtimes: without it every file lands with a fresh timestamp, so unchanged
|
||||
// files get a new Last-Modified on every deploy and every conditional request revalidates
|
||||
// into a full response instead of a 304.
|
||||
'flags' => 'rzt',
|
||||
'options' => ['delete', 'links', 'chmod=D755,F664'],
|
||||
'timeout' => 300,
|
||||
];
|
||||
|
||||
+114
-1
@@ -75,6 +75,58 @@ AddType image/webp .webp
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
# Brotli, where the host has the module. It beats gzip by roughly 15-20% on the theme's
|
||||
# minified css/js at no measurable cost, and falls back to the mod_deflate block above for
|
||||
# clients that do not announce `br` - which is also what happens wholesale if the module is
|
||||
# missing, because the whole block is then skipped.
|
||||
#
|
||||
# The type list is deliberately the same one mod_deflate gets: `no-gzip` below switches gzip
|
||||
# off for every br-capable client, so a type listed for DEFLATE only would reach those
|
||||
# clients uncompressed.
|
||||
<IfModule mod_brotli.c>
|
||||
# Both filters would otherwise run for a client announcing `br, gzip` and gzip the brotli
|
||||
# output on top of itself. mod_deflate honours `no-gzip`, so brotli wins where it is
|
||||
# offered and gzip keeps everyone else.
|
||||
<IfModule mod_setenvif.c>
|
||||
SetEnvIfNoCase Accept-Encoding "(^|[\s,])br($|[\s,;])" no-gzip
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_filter.c>
|
||||
AddOutputFilterByType BROTLI_COMPRESS application/atom+xml \
|
||||
application/javascript \
|
||||
application/json \
|
||||
application/ld+json \
|
||||
application/manifest+json \
|
||||
application/rdf+xml \
|
||||
application/rss+xml \
|
||||
application/schema+json \
|
||||
application/vnd.geo+json \
|
||||
application/vnd.ms-fontobject \
|
||||
application/x-font-ttf \
|
||||
application/x-javascript \
|
||||
application/x-web-app-manifest+json \
|
||||
application/xhtml+xml \
|
||||
application/xml \
|
||||
font/eot \
|
||||
font/opentype \
|
||||
image/bmp \
|
||||
image/svg+xml \
|
||||
image/vnd.microsoft.icon \
|
||||
image/x-icon \
|
||||
text/cache-manifest \
|
||||
text/css \
|
||||
text/html \
|
||||
text/javascript \
|
||||
text/plain \
|
||||
text/vcard \
|
||||
text/vnd.rim.location.xloc \
|
||||
text/vtt \
|
||||
text/x-component \
|
||||
text/x-cross-domain-policy \
|
||||
text/xml
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
### End: Compression ###
|
||||
|
||||
|
||||
@@ -139,6 +191,16 @@ AddType image/webp .webp
|
||||
|
||||
</IfModule>
|
||||
|
||||
# Webpack writes an 8-hex-character content hash into the filename of everything it emits, so
|
||||
# these URLs can never change meaning - a new build is a new URL. `immutable` stops browsers
|
||||
# revalidating them on reload, and overrides the per-type rules above, which would otherwise
|
||||
# expire the hashed fonts after a month.
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.[0-9a-f]{8}\.(js|css|woff2?|jpe?g|png|gif|svg|webp|avif)$">
|
||||
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
### End: Browser caching of resource files ###
|
||||
|
||||
|
||||
@@ -241,7 +303,58 @@ AddDefaultCharset utf-8
|
||||
# Enable URL rewriting
|
||||
RewriteEngine On
|
||||
|
||||
# Force SSL
|
||||
# Legacy and campaign domains, consolidated onto their current host. These have to stay
|
||||
# ahead of the Force SSL block and of TYPO3's catch-all at the end of this file, or a
|
||||
# request for one of them reaches index.php before it is ever redirected.
|
||||
#
|
||||
# Maintained here rather than on the host: the deploy rsyncs this file into every release
|
||||
# (deploy.php), so anything edited live is gone after the next one.
|
||||
RewriteRule ^buchung/(.*)$ https://bpn.ep-reisen.de/$1 [R=302,L,QSA]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^cllt\-touristik\.at$ [NC]
|
||||
RewriteRule ^(.*) https://www.cllt-touristik.at/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^alpinevacation\.ch$ [NC]
|
||||
RewriteRule ^(.*) https://www.alpinevacation.ch/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^(www\.)?semesterendeskireisen\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.semesterende-skireisen.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^semesterende\-skireisen\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.semesterende-skireisen.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^snowzone\.net$ [NC]
|
||||
RewriteRule ^(.*) https://www.snowzone.net/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^ski\-boarderweek\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.ski-boarderweek.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ski\-boarderweek\.(com|eu)$ [NC]
|
||||
RewriteRule ^(.*) https://www.ski-boarderweek.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} skiboarderweek\.(de|eu)$ [NC]
|
||||
RewriteRule ^(.*) https://www.ski-boarderweek.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^ski\-wochenenden\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.ski-wochenenden.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^ep\-events\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.ep-events.de/$1 [L,R=301]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^ep\-reisen\.de$ [NC]
|
||||
RewriteRule ^(.*) https://www.ep-reisen.de/$1 [L,R=301]
|
||||
|
||||
# The dropped path is deliberate: snow-break sends every deep link to the snowbreak
|
||||
# landing page, unlike unichamp below, which keeps it.
|
||||
RewriteCond %{HTTP_HOST} ^(www\.)?snow\-break\.(de|com) [NC]
|
||||
RewriteRule (.*) https://snowbreak.ep-reisen.de/ [R=302,L]
|
||||
|
||||
# Matches the bare domain as well, so it lands on snowbreak in one hop - a separate
|
||||
# unichamp.de -> www.unichamp.de rule used to run first and cost an extra 301.
|
||||
RewriteCond %{HTTP_HOST} ^(www\.)?unichamp\.de$ [NC]
|
||||
RewriteRule ^(.*) https://snowbreak.ep-reisen.de/$1 [L,R=302]
|
||||
|
||||
# Force SSL
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteCond %{HTTP:X-Forwarded-Proto} =http
|
||||
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]
|
||||
|
||||
+16
-10
@@ -1,17 +1,23 @@
|
||||
config.tx_replacer {
|
||||
enable_regex = 1
|
||||
|
||||
# The quote character is captured rather than matched literally, because the
|
||||
# @font-face block in Page/Main.typoscript writes its url() values with single
|
||||
# quotes - anchoring on `"` alone would skip every font.
|
||||
# typo3conf is scoped to ep_theme/Resources/Public on purpose: it keeps other
|
||||
# extensions, Configuration/ and Resources/Private/ out of the CDN.
|
||||
# The icon sprite is excluded: SVG <use href="...#icon"> may only reference a
|
||||
# document from the same origin - browsers block an external sprite on the CDN
|
||||
# host outright, and no CORS header on bunny.net changes that. It has to stay
|
||||
# on the origin domain or every icon renders empty.
|
||||
# Only images go to the CDN. The theme's own css/js/fonts are served by the production
|
||||
# host: they are already minified by Webpack, gzipped by mod_deflate and content-hashed
|
||||
# for a year of immutable browser caching, and keeping them on the page's own origin
|
||||
# means they arrive over the connection the browser has already opened rather than
|
||||
# costing a second DNS lookup and TLS handshake on the critical path.
|
||||
#
|
||||
# It also keeps them clear of the pull zone's habit of occasionally wedging a single
|
||||
# cache key (10.0s -> 504 errorcode 106 until that key is purged, while the origin
|
||||
# serves the same bytes in under 100ms). For an image that degrades one element; for
|
||||
# js/main.<hash>.js it would take every visitor's JavaScript down.
|
||||
#
|
||||
# typo3temp is scoped to generated images on purpose - assets/compressed holds other
|
||||
# extensions' concatenated css/js, which belongs on the origin for the same reason.
|
||||
# The quote character is captured rather than matched literally, so url() values
|
||||
# written with single quotes are rewritten too.
|
||||
search {
|
||||
10 = /(["'])\/?(fileadmin|typo3temp|uploads|typo3conf\/ext\/ep_theme\/Resources\/Public(?!\/images\/icons_sprite\.svg))/
|
||||
10 = /(["'])\/?(fileadmin|uploads|typo3temp\/assets\/(?:_processed_|images))/
|
||||
}
|
||||
|
||||
replace {
|
||||
|
||||
@@ -31,6 +31,15 @@ page {
|
||||
40.value = ">
|
||||
}
|
||||
|
||||
# Every image on the page comes from the CDN host (see Extensions/Replacer.typoscript),
|
||||
# so a cold visitor pays a full DNS + TCP + TLS handshake - measured at 50-70ms - before
|
||||
# the first one can start downloading. `preconnect` gets that out of the way while the
|
||||
# HTML is still parsing. No `crossorigin`: images are fetched without CORS, and an
|
||||
# anonymous-vs-CORS mismatch would open a second connection instead of reusing this one.
|
||||
# Fonts, which would need it, are served by this host now.
|
||||
headerData.100 = TEXT
|
||||
headerData.100.value = <link rel="preconnect" href="https://cdn.ep-reisen.de">
|
||||
|
||||
headerData.333 = TEXT
|
||||
headerData.333.value (
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon.ico">
|
||||
@@ -57,13 +66,21 @@ page {
|
||||
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-196x196.png" sizes="196x196">
|
||||
)
|
||||
|
||||
# disableCompression keeps ResourceCompressor's hands off the Encore entries: Webpack
|
||||
# already minifies them, and copying them to typo3temp/assets/compressed only replaces the
|
||||
# content hash with a name derived from md5(abspath + mtime + size), which changes on every
|
||||
# deploy even when the bytes do not - defeating the immutable caching the hash exists for.
|
||||
# RequestHandler turns this into `compress => false`, which PageRendererHooks forwards to
|
||||
# typo3_encore's TagRenderer (whose own default it would otherwise override).
|
||||
includeCSS {
|
||||
main = typo3_encore:EpThemeConfig:main
|
||||
main.disableCompression = 1
|
||||
}
|
||||
|
||||
includeJSFooter {
|
||||
main = typo3_encore:EpThemeConfig:main
|
||||
main.defer = 1
|
||||
main.disableCompression = 1
|
||||
}
|
||||
|
||||
10 = FLUIDTEMPLATE
|
||||
@@ -150,45 +167,6 @@ page {
|
||||
settings < plugin.tx_eptheme.settings
|
||||
}
|
||||
|
||||
CSS_inlineStyle (
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-300.woff2') format('woff2'),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-300.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-regular.woff2') format('woff2'),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-700.woff2') format('woff2'),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-700.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-900.woff2') format('woff2'),
|
||||
url('/typo3conf/ext/ep_theme/Resources/Public/fonts/lato-v17-latin-900.woff') format('woff');
|
||||
}
|
||||
)
|
||||
|
||||
headerData.990 = TEXT
|
||||
headerData.990 {
|
||||
value = <script src="{$plugin.tx_eptheme.settings.cookieFirstScriptUrl}"></script>
|
||||
|
||||
+11
@@ -1,5 +1,16 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import L from 'leaflet'
|
||||
import iconUrl from 'leaflet/dist/images/marker-icon.png'
|
||||
import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'
|
||||
import shadowUrl from 'leaflet/dist/images/marker-shadow.png'
|
||||
|
||||
// Icon.Default finds its images by reading the url() off .leaflet-default-icon-path and
|
||||
// stripping a literal `marker-icon.png` off the end - which a content-hashed filename never
|
||||
// matches, and the `link[href$="leaflet.css"]` fallback does not exist either because the
|
||||
// CSS is inlined into the bundle. Dropping the _getIconUrl override falls back to plain
|
||||
// option lookup, so the URLs Webpack emitted are used as they are.
|
||||
delete L.Icon.Default.prototype._getIconUrl
|
||||
L.Icon.Default.mergeOptions({ iconUrl, iconRetinaUrl, shadowUrl })
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
@use "sass:meta";
|
||||
|
||||
// Outside the cascade layers on purpose: @font-face is invalid inside @layer, which is why
|
||||
// _base.scss leaves base/fonts out. Loading it here lets Webpack resolve the url()s to
|
||||
// content-hashed filenames, and replaces the hand-maintained @font-face block that used to
|
||||
// live in ep_theme Page/Main.typoscript.
|
||||
@include meta.load-css("base/fonts");
|
||||
|
||||
@include meta.load-css("tailwindcss/base");
|
||||
@include meta.load-css("base");
|
||||
|
||||
|
||||
+15
-8
@@ -12,7 +12,10 @@ Encore
|
||||
.addEntry('main', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/main.js')
|
||||
.addStyleEntry('rte', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/scss/rte.scss')
|
||||
.splitEntryChunks()
|
||||
.enableSingleRuntimeChunk()
|
||||
// There is only one JS entry, so a shared runtime chunk buys nothing and costs a separate
|
||||
// 1.6KB request that every other bundle hard-depends on. Inlining it into main.js removes
|
||||
// both, and stops the `rte` style entry from dragging a JS file in behind it.
|
||||
.disableSingleRuntimeChunk()
|
||||
.enableSassLoader(options => {
|
||||
// Opt into the modern Sass JS API (sass-loader defaults to the legacy one,
|
||||
// which Dart Sass 2.0 removes). Encore still passes `outputStyle`, which
|
||||
@@ -37,20 +40,24 @@ Encore
|
||||
}
|
||||
})
|
||||
.copyFiles([
|
||||
{ from: './public/typo3conf/ext/ep_theme/Resources/Private/Assets/fonts', to: 'fonts/[name].[ext]' },
|
||||
{ from: './public/typo3conf/ext/ep_theme/Resources/Private/Assets/images', to: 'images/[name].[ext]' },
|
||||
{ from: './public/typo3conf/ext/ep_theme/Resources/Private/Assets/images/icons', to: 'images/icons/[name].[ext]' },
|
||||
{ from: './node_modules/leaflet/dist/images', to: 'images/[name].[ext]' },
|
||||
])
|
||||
// The content hash is what lets .htaccess serve these as `immutable` for a year: a new
|
||||
// build is a new URL, so a browser never has to revalidate one. It also replaces TYPO3's
|
||||
// `?<mtime>` query buster, which changed on every deploy even when the bytes did not.
|
||||
// `rte` stays unhashed - Configuration/RTE/*.yaml reference rte.css by literal path.
|
||||
.configureFilenames({
|
||||
js: 'js/[name].js',
|
||||
css: 'css/[name].css',
|
||||
js: Encore.isProduction() ? 'js/[name].[contenthash:8].js' : 'js/[name].js',
|
||||
css: (pathData) => Encore.isProduction() && pathData.chunk.name !== 'rte'
|
||||
? 'css/[name].[contenthash:8].css'
|
||||
: 'css/[name].css',
|
||||
})
|
||||
.configureImageRule({
|
||||
filename: 'images/[name][ext]',
|
||||
filename: 'images/[name].[contenthash:8][ext]',
|
||||
})
|
||||
.configureFontRule({
|
||||
filename: 'fonts/[name][ext]',
|
||||
filename: 'fonts/[name].[contenthash:8][ext]',
|
||||
})
|
||||
.configureBabelPresetEnv((config) => {
|
||||
config.shippedProposals = true
|
||||
@@ -59,7 +66,7 @@ Encore
|
||||
const EpThemeConfig = Encore.getWebpackConfig();
|
||||
|
||||
EpThemeConfig.name = 'EpThemeConfig';
|
||||
EpThemeConfig.output.chunkFilename = 'js/[name].[chunkhash:8].js';
|
||||
EpThemeConfig.output.chunkFilename = 'js/[name].[contenthash:8].js';
|
||||
|
||||
Encore.reset();
|
||||
|
||||
|
||||
Reference in new issue
Block a user