Correctly inlining the global loader style

master
WolverinDEV 2021-03-17 13:41:01 +01:00
parent 61da22895f
commit 503ca5db8b
6 changed files with 311 additions and 165 deletions

View File

@ -1,3 +1,3 @@
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./index.scss";
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./loader.scss";
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./overlay.css";
import "./index.scss";
import "./loader.scss";
import "./overlay.css";

View File

@ -1,138 +1,162 @@
$setup-time-normal: 80s / 24; /* 24 frames / sec; the initial sequence is 80 frames */
$setup-time-halloween: 323s / 24;
$loop-time-halloween: 25s / 24;
:global {
$setup-time-normal: 80s / 24; /* 24 frames / sec; the initial sequence is 80 frames */
$setup-time-halloween: 323s / 24;
$loop-time-halloween: 25s / 24;
#loader-overlay {
position: absolute;
overflow: hidden;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #1e1e1e;
user-select: none;
z-index: 10000000;
display: flex;
flex-direction: column;
justify-content: center;
-webkit-app-region: drag;
.container {
flex-shrink: 0;
display: block;
position: relative;
width: 1000px;
height: 1000px;
align-self: center;
margin-bottom: 10vh;
transition-duration: .5s;
img {
user-select: none;
}
}
.setup, .idle {
#loader-overlay {
position: absolute;
overflow: hidden;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: none;
background: #1e1e1e;
user-select: none;
z-index: 10000000;
display: flex;
flex-direction: column;
justify-content: center;
-webkit-app-region: drag;
.container {
flex-shrink: 0;
&.visible {
display: block;
}
}
position: relative;
.setup.visible {
&.normal {
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-normal forwards;
width: 1000px;
height: 1000px;
align-self: center;
margin-bottom: 10vh;
transition-duration: .5s;
img {
user-select: none;
}
}
&.halloween {
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-halloween forwards;
}
}
.idle.animation-normal {
img {
position: absolute;
}
.steam {
.setup, .idle {
position: absolute;
top: 282px;
left: 380px;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 249px;
height: 125px;
background: url("../images/steam.png") 0 0;
display: none;
animation: sprite-steam 2.5s steps(50) forwards infinite;
&.visible {
display: block;
}
}
}
&.finishing {
.idle {
.setup.visible {
&.normal {
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-normal forwards;
}
&.halloween {
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-halloween forwards;
}
}
.idle.animation-normal {
img {
position: absolute;
}
.steam {
display: none;
}
position: absolute;
.bowl {
animation: swipe-out-bowl .5s both;
}
top: 282px;
left: 380px;
.text {
animation: swipe-out-text .5s .12s both;
width: 249px;
height: 125px;
background: url("../images/steam.png") 0 0;
animation: sprite-steam 2.5s steps(50) forwards infinite;
}
}
pointer-events: none;
animation: overlay-fade .3s .2s both;
&.finishing {
.idle {
.steam {
display: none;
}
.bowl {
animation: swipe-out-bowl .5s both;
}
.text {
animation: swipe-out-text .5s .12s both;
}
}
pointer-events: none;
animation: overlay-fade .3s .2s both;
}
.loader-stage {
position: absolute;
left: 5px;
bottom: 5px;
font-size: 12px;
font-family: monospace;
color: #999;
}
}
.loader-stage {
position: absolute;
/* Automated loader timeout */
#loader-overlay:not(.initialized) + #critical-load:not(.shown) {
display: block !important;
opacity: 0;
left: 5px;
bottom: 5px;
animation: loader-setup-timeout 0s ease-in $setup-time-normal forwards;
font-size: 12px;
font-family: monospace;
.error::before {
content: 'Failed to startup loader!';
}
color: #999;
.detail::before {
content: 'Lookup the console for more details';
}
}
}
@media all and (max-width: 850px) {
#loader-overlay .container {
transform: scale(.5);
:global {
#loader-overlay .container {
transform: scale(.5);
}
}
}
@media all and (max-height: 700px) {
#loader-overlay .container {
transform: scale(.5);
:global {
#loader-overlay .container {
transform: scale(.5);
}
}
}
@media all and (max-width: 400px) {
#loader-overlay .container {
transform: scale(.3);
:global {
#loader-overlay .container {
transform: scale(.3);
}
}
}
@ -192,23 +216,6 @@ $loop-time-halloween: 25s / 24;
}
}
/* Automated loader timeout */
#loader-overlay:not(.initialized) + #critical-load:not(.shown) {
display: block !important;
opacity: 0;
animation: loader-setup-timeout 0s ease-in $setup-time-normal forwards;
.error::before {
content: 'Failed to startup loader!';
}
.detail::before {
content: 'Lookup the console for more details';
}
}
@keyframes loader-setup-timeout {
to {
opacity: 1;

View File

@ -1,64 +1,66 @@
#overlay-no-js, #critical-load {
z-index: 100000000;
display: none;
position: fixed;
:global {
#overlay-no-js, #critical-load {
z-index: 100000000;
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #1e1e1e;
text-align: center;
background: #1e1e1e;
text-align: center;
-webkit-app-region: drag;
-webkit-app-region: drag;
h1, h3, a {
-webkit-app-region: no-drag;
}
h1, h3, a {
-webkit-app-region: no-drag;
}
.container {
position: relative;
display: inline-block;
.container {
position: relative;
display: inline-block;
top: 20%;
}
top: 20%;
}
&.shown {
display: block;
}
}
#critical-load {
img {
height: 12em
}
.error {
color: #bd1515;
margin-bottom: 0
}
.detail {
color: #696363;
margin-top: .5em
}
}
@media (max-height: 750px) {
#critical-load .container {
top: unset;
&.shown {
display: block;
}
}
#critical-load {
font-size: .8rem;
img {
height: 12em
}
flex-direction: column;
justify-content: center;
.error {
color: #bd1515;
margin-bottom: 0
}
.detail {
color: #696363;
margin-top: .5em
}
}
#critical-load.shown {
display: flex;
@media (max-height: 750px) {
#critical-load .container {
top: unset;
}
#critical-load {
font-size: .8rem;
flex-direction: column;
justify-content: center;
}
#critical-load.shown {
display: flex;
}
}
}

View File

@ -66,7 +66,6 @@
"node-sass": "^4.14.1",
"potpack": "^1.0.1",
"raw-loader": "^4.0.0",
"react-dev-utils": "^11.0.4",
"sass": "1.22.10",
"sass-loader": "^8.0.2",
"sha256": "^0.2.0",

View File

@ -11,7 +11,7 @@ const webpack = require("webpack");
import { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator";
const ManifestGenerator = require("./webpack/ManifestPlugin");
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
const HtmlWebpackInlineSourcePlugin = require("./webpack/HtmlWebpackInlineSource");
import HtmlWebpackPlugin from "html-webpack-plugin";
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
@ -155,7 +155,7 @@ export const config = async (target: "web" | "client"): Promise<Configuration &
}
}),
generateIndexPlugin(target),
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.*/]),
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
].filter(e => !!e),
module: {

View File

@ -0,0 +1,138 @@
const escapeRegex = require('escape-string-regexp');
const path = require('path');
const slash = require('slash');
const sourceMapUrl = require('source-map-url');
function HtmlWebpackInlineSourcePlugin (htmlWebpackPlugin) {
this.htmlWebpackPlugin = htmlWebpackPlugin;
}
HtmlWebpackInlineSourcePlugin.prototype.apply = function (compiler) {
const self = this;
// Hook into the html-webpack-plugin processing
compiler.hooks.compilation.tap('html-webpack-inline-source-plugin', compilation => {
self.htmlWebpackPlugin
.getHooks(compilation)
.alterAssetTagGroups.tapAsync('html-webpack-inline-source-plugin', (htmlPluginData, callback) => {
if (!htmlPluginData.plugin.options.inlineSource) {
return callback(null, htmlPluginData);
}
const regexStr = htmlPluginData.plugin.options.inlineSource;
const result = self.processTags(compilation, regexStr, htmlPluginData);
callback(null, result);
});
});
};
HtmlWebpackInlineSourcePlugin.prototype.processTags = function (compilation, regexStr, pluginData) {
const self = this;
const bodyTags = [];
const headTags = [];
const regex = new RegExp(regexStr);
const filename = pluginData.plugin.options.filename;
pluginData.headTags.forEach(function (tag) {
headTags.push(self.processTag(compilation, regex, tag, filename));
});
pluginData.bodyTags.forEach(function (tag) {
bodyTags.push(self.processTag(compilation, regex, tag, filename));
});
return { headTags: headTags, bodyTags: bodyTags, plugin: pluginData.plugin, outputName: pluginData.outputName };
};
HtmlWebpackInlineSourcePlugin.prototype.resolveSourceMaps = function (compilation, assetName, asset) {
let source = asset.source();
const out = compilation.outputOptions;
// Get asset file absolute path
const assetPath = path.join(out.path, assetName);
// Extract original sourcemap URL from source string
if (typeof source !== 'string') {
source = source.toString();
}
const mapUrlOriginal = sourceMapUrl.getFrom(source);
// Return unmodified source if map is unspecified, URL-encoded, or already relative to site root
if (!mapUrlOriginal || mapUrlOriginal.indexOf('data:') === 0 || mapUrlOriginal.indexOf('/') === 0) {
return source;
}
// Figure out sourcemap file path *relative to the asset file path*
const assetDir = path.dirname(assetPath);
const mapPath = path.join(assetDir, mapUrlOriginal);
const mapPathRelative = path.relative(out.path, mapPath);
// Starting with Node 6, `path` module throws on `undefined`
const publicPath = out.publicPath || '';
// Prepend Webpack public URL path to source map relative path
// Calling `slash` converts Windows backslashes to forward slashes
const mapUrlCorrected = slash(path.join(publicPath, mapPathRelative));
// Regex: exact original sourcemap URL, possibly '*/' (for CSS), then EOF, ignoring whitespace
const regex = new RegExp(escapeRegex(mapUrlOriginal) + '(\\s*(?:\\*/)?\\s*$)');
// Replace sourcemap URL and (if necessary) preserve closing '*/' and whitespace
return source.replace(regex, function (match, group) {
return mapUrlCorrected + group;
});
};
HtmlWebpackInlineSourcePlugin.prototype.processTag = function (compilation, regex, tag, filename) {
let assetUrl;
let preTag = tag;
// inline js
if (tag.tagName === 'script' && tag.attributes && regex.test(tag.attributes.src)) {
assetUrl = tag.attributes.src;
tag = {
tagName: 'script',
closeTag: true,
attributes: {
type: 'text/javascript'
}
};
// inline css
} else if (tag.tagName === 'link' && regex.test(tag.attributes.href)) {
assetUrl = tag.attributes.href;
tag = {
tagName: 'style',
closeTag: true,
attributes: {
type: 'text/css'
}
};
}
if (assetUrl) {
// Strip public URL prefix from asset URL to get Webpack asset name
const publicUrlPrefix = compilation.outputOptions.publicPath || '';
// if filename is in subfolder, assetUrl should be prepended folder path
if (path.basename(filename) !== filename) {
assetUrl = path.dirname(filename) + '/' + assetUrl;
}
const assetName = path.posix.relative(publicUrlPrefix, assetUrl);
const asset = getAssetByName(compilation.assets, assetName);
if (compilation.assets[assetName] !== undefined) {
const updatedSource = this.resolveSourceMaps(compilation, assetName, asset);
tag.innerHTML = (tag.tagName === 'script') ? updatedSource.replace(/(<)(\/script>)/g, '\\x3C$2') : updatedSource;
}else{
return preTag;
}
}
return tag;
};
function getAssetByName (assests, assetName) {
for (let key in assests) {
if (assests.hasOwnProperty(key)) {
let processedKey = path.posix.relative('', key);
if (processedKey === assetName) {
return assests[key];
}
}
}
}
module.exports = HtmlWebpackInlineSourcePlugin;