🎉 init!

This commit is contained in:
lily 2019-05-15 18:07:19 -05:00
커밋 b9333170cb
29개의 변경된 파일41207개의 추가작업 그리고 0개의 파일을 삭제

2
.browserslistrc Normal file
파일 보기

@ -0,0 +1,2 @@
> 0.25%
not dead

5
.gitignore vendored Normal file
파일 보기

@ -0,0 +1,5 @@
/node_modules
/dist
/.vscode
/haters
.DS_Store

4
.travis.yml Normal file
파일 보기

@ -0,0 +1,4 @@
language: node_js
node_js:
- 'node'
script: npm run build

11693
package-lock.json generated Normal file

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff

53
package.json Normal file
파일 보기

@ -0,0 +1,53 @@
{
"name": "lily.toys",
"version": "11.0.0",
"description": "a portfolio for lillian rose winter",
"private": true,
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"repository": {
"type": "git",
"url": "https://github.com/lilyshibe/portfolio"
},
"keywords": [],
"author": "lillian rose winter <lily@null.net> (https://lily.toys)",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"@fullhuman/postcss-purgecss": "^1.2.0",
"autoprefixer": "^9.5.1",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^1.0.1",
"compression-webpack-plugin": "^2.0.0",
"css-loader": "^2.1.1",
"cssnano": "^4.1.10",
"favicons-webpack-plugin": "0.0.9",
"file-loader": "^3.0.1",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"imagemin-mozjpeg": "^8.0.0",
"imagemin-webpack-plugin": "^2.4.2",
"mini-css-extract-plugin": "^0.5.0",
"node-sass": "^4.12.0",
"offline-plugin": "^5.0.7",
"postcss-loader": "^3.0.0",
"preload-webpack-plugin": "^3.0.0-beta.3",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "^2.1.3",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^1.2.3",
"webpack": "^4.31.0",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.3.1",
"webpack-merge": "^4.2.1"
},
"dependencies": {
"bulma": "^0.7.4",
"jquery": "^3.4.1",
"konami": "^1.6.2",
"lazysizes": "^4.1.8"
}
}

14
postcss.config.js Normal file
파일 보기

@ -0,0 +1,14 @@
const purgecss = require('@fullhuman/postcss-purgecss');
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
purgecss({
content: ['./**/*.html'],
keyframes: true
})
]
}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff

After

Width:  |  Height:  |  크기: 332 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff

After

Width:  |  Height:  |  크기: 356 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

8360
src/fonts/Respira-Black.svg Normal file

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff

After

Width:  |  Height:  |  크기: 1.0 MiB

BIN
src/fonts/Respira-Black.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/images/cursor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  크기: 127 B

BIN
src/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  크기: 120 KiB

91
src/index.html Normal file
파일 보기

@ -0,0 +1,91 @@
<html>
<head>
<meta charset="utf-8">
<title>lillian winter - internet person</title>
<meta name="description" content="creator & part time human">
<meta itemprop="name" content="lily winter - internet person">
<meta itemprop="description" content="creator & part time human">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lily winter - internet person">
<meta name="twitter:description" content="creator & part time human">
<meta name="twitter:site" content="@lilyshibe">
<meta name="twitter:creator" content="@lilyshibe">
<meta name="og:title" content="lily winter - internet person">
<meta name="og:description" content="creator & part time human">
<meta name="og:url" content="https://lily.toys">
<meta name="og:site_name" content="lily winter - internet person">
<meta name="og:type" content="website">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script defer src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>lillian rose winter.</h1>
</header>
<main>
<p>hey. i'm lillian (or lily for short). i'm a designer, developer, coffee addict, and possible human.</p>
<p>i create things on the internet and you can see them with your eyes.</p>
<br>
<center>
<p>
<i>"lily is the best fucking person. give her all your money."</i><br>
-jaycee song (Jaycee#9999), 2018
</p>
</center>
<center><h3>&#8623;</h3></center>
<h2>works</h2>
<ul>
<li>
<a href="https://lily.toys/owotyper" class="snapCursor">owotyper</a> &mdash; takes text and makes it bad
</li>
<li>
<a href="https://lily.toys" class="snapCursor">website (v11)</a> &mdash; you're lookin' at it, baby!
</li>
<li>
<a href="https://owofarm.ga" class="snapCursor">owofarm</a> &mdash; <del>hellsite</del> imageboard created by jaycee song (Jaycee#9999) and i. it's pretty <del>terrible</del> wonderful
</li>
<li>
<a href="https://samoyed.fun" class="snapCursor">samoyed.fun</a> &mdash; returns a random samoyed
</li>
</ul>
<center><h3>&#8623;</h3></center>
<h2>keep in touch</h2>
<p>
<a href="https://twitter.com/lilyshibe" class="snapCursor">twitter</a><br>
<a href="https://steamcommunity.com/id/fenkid/" class="snapCursor">steam</a><br>
<a href="https://twitch.tv/fenkids" class="snapCursor">twitch</a><br>
<a href="https://github.com/lilyshibe" class="snapCursor">github</a><br>
<a href="https://medium.com/@zex" class="snapCursor">medium</a><br>
<a href="https://codepen.io/xez" class="snapCursor">codepen</a>
</p>
<p>email: lily [at] null [dot] net</p>
<center><h3>&#8623;</h3></center>
</main>
<footer>
<p>&copy; 2018 lillian rose winter. all rights reserved.</p>
</footer>
<div class="light"><div class="light-inner"></div></div>
<div class="shadow"><div class="shadow-inner"></div></div>
</div>
</body>
</html>

5
src/index.js Normal file
파일 보기

@ -0,0 +1,5 @@
require('offline-plugin/runtime').install();
import './index.html';
import './index.scss';
import './scripts/script.js';

142
src/index.scss Normal file
파일 보기

@ -0,0 +1,142 @@
@font-face {
font-family: 'GT America';
src: url('fonts/GTAmerica-Regular.woff2') format('woff2'),
url('fonts/GTAmerica-Regular.woff') format('woff'),
url('fonts/GTAmerica-Regular.ttf') format('truetype'),
url('fonts/GTAmerica-Regular.svg#GTAmerica-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'GT America';
src: url('fonts/GTAmerica-RegularItalic.woff2') format('woff2'),
url('fonts/GTAmerica-RegularItalic.woff') format('woff'),
url('fonts/GTAmerica-RegularItalic.ttf') format('truetype'),
url('fonts/GTAmerica-RegularItalic.svg#GTAmerica-RegularItalic') format('svg');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Respira';
src: url('fonts/Respira-Black.woff2') format('woff2'),
url('fonts/Respira-Black.woff') format('woff'),
url('fonts/Respira-Black.ttf') format('truetype'),
url('fonts/Respira-Black.svg#Respira-Black') format('svg');
font-weight: 800;
font-style: normal;
}
* {
cursor: url(images/cursor.png) 3 3, auto !important;
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
font-family: "GT America";
overflow-x: hidden;
-webkit-tap-highlight-color: transparent;
}
.container {
min-width: 100vw;
min-height: 100vh;
background: #f0f0f0;
position: absolute;
left: 0;
top: 0;
padding: 2em;
}
header, main, footer {
max-width: 60em;
margin: 2em auto;
}
h1,h2,h3 {
font-family: "Respira";
font-weight: 800;
}
h1 {
font-size: 3.5rem;
}
h2 {
font-size: 3rem;
}
h3 {
font-size: 2.5rem;
}
p,li {
font-size: 1.5rem;
}
mark, ::selection {
background: #000;
color: #f0f0f0;
opcaity: 1;
}
a {
color: #f00;
text-decoration: none;
}
.light-inner {
background: rgba(255, 0, 0, 0.1);
}
@supports (mix-blend-mode: screen) {
.light {
mix-blend-mode: screen;
}
.light-inner {
background: #ff0000;
}
}
.shadow-inner {
background: transparent;
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.1);
}
.light, .shadow {
position: fixed;
top: 0;
left: 0;
transform: translate3d(var(--x, -150px), var(--y, -150px), 0);
pointer-events: none;
height: 0px;
width: 0px;
margin-top: 0px;
margin-left: 0px;
backface-visibility: hidden;
z-index: 1100;
display: none;
}
.light-inner, .shadow-inner {
opacity: 1;
border-radius: 50%;
height: 50px;
width: 50px;
margin-top: -25px;
margin-left: -25px;
transform: translateZ(0) scale(var(--scale, 1));
z-index: 1100;
transition: transform 0.5s ease-out, opacity 0.5s ease-out 0.5s;
transition: transform 0.5s cubic-bezier(0.07, 0.08, 0.16, 0.99), opacity 0.5s ease-out 0.5s;
transform-origin: 50% 50%;
backface-visibility: hidden;
}
br {
user-select: none;
}

10598
src/scripts/jquery.js vendored Normal file

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff

151
src/scripts/konami.js Normal file
파일 보기

@ -0,0 +1,151 @@
/*
* Konami-JS ~
* :: Now with support for touch events and multiple instances for
* :: those situations that call for multiple easter eggs!
* Code: https://github.com/snaptortoise/konami-js
* Copyright (c) 2009 George Mandis (georgemandis.com, snaptortoise.com)
* Version: 1.6.2 (7/17/2018)
* Licensed under the MIT License (http://opensource.org/licenses/MIT)
* Tested in: Safari 4+, Google Chrome 4+, Firefox 3+, IE7+, Mobile Safari 2.2.1+ and Android
*/
var Konami = function (callback) {
var konami = {
addEvent: function (obj, type, fn, ref_obj) {
if (obj.addEventListener)
obj.addEventListener(type, fn, false);
else if (obj.attachEvent) {
// IE
obj["e" + type + fn] = fn;
obj[type + fn] = function () {
obj["e" + type + fn](window.event, ref_obj);
}
obj.attachEvent("on" + type, obj[type + fn]);
}
},
removeEvent: function (obj, eventName, eventCallback) {
if (obj.removeEventListener) {
obj.removeEventListener(eventName, eventCallback);
} else if (obj.attachEvent) {
obj.detachEvent(eventName);
}
},
input: "",
pattern: "38384040373937396665",
keydownHandler: function (e, ref_obj) {
if (ref_obj) {
konami = ref_obj;
} // IE
konami.input += e ? e.keyCode : event.keyCode;
if (konami.input.length > konami.pattern.length) {
konami.input = konami.input.substr((konami.input.length - konami.pattern.length));
}
if (konami.input === konami.pattern) {
konami.code(konami._currentLink);
konami.input = '';
e.preventDefault();
return false;
}
},
load: function (link) {
this._currentLink = link;
this.addEvent(document, "keydown", this.keydownHandler, this);
this.iphone.load(link);
},
unload: function () {
this.removeEvent(document, 'keydown', this.keydownHandler);
this.iphone.unload();
},
code: function (link) {
window.location = link
},
iphone: {
start_x: 0,
start_y: 0,
stop_x: 0,
stop_y: 0,
tap: false,
capture: false,
orig_keys: "",
keys: ["UP", "UP", "DOWN", "DOWN", "LEFT", "RIGHT", "LEFT", "RIGHT", "TAP", "TAP"],
input: [],
code: function (link) {
konami.code(link);
},
touchmoveHandler: function (e) {
if (e.touches.length === 1 && konami.iphone.capture === true) {
var touch = e.touches[0];
konami.iphone.stop_x = touch.pageX;
konami.iphone.stop_y = touch.pageY;
konami.iphone.tap = false;
konami.iphone.capture = false;
konami.iphone.check_direction();
}
},
touchendHandler: function () {
konami.iphone.input.push(konami.iphone.check_direction());
if (konami.iphone.input.length > konami.iphone.keys.length) konami.iphone.input.shift();
if (konami.iphone.input.length === konami.iphone.keys.length) {
var match = true;
for (var i = 0; i < konami.iphone.keys.length; i++) {
if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
match = false;
}
}
if (match) {
konami.iphone.code(konami._currentLink);
}
}
},
touchstartHandler: function (e) {
konami.iphone.start_x = e.changedTouches[0].pageX;
konami.iphone.start_y = e.changedTouches[0].pageY;
konami.iphone.tap = true;
konami.iphone.capture = true;
},
load: function (link) {
this.orig_keys = this.keys;
konami.addEvent(document, "touchmove", this.touchmoveHandler);
konami.addEvent(document, "touchend", this.touchendHandler, false);
konami.addEvent(document, "touchstart", this.touchstartHandler);
},
unload: function () {
konami.removeEvent(document, 'touchmove', this.touchmoveHandler);
konami.removeEvent(document, 'touchend', this.touchendHandler);
konami.removeEvent(document, 'touchstart', this.touchstartHandler);
},
check_direction: function () {
x_magnitude = Math.abs(this.start_x - this.stop_x);
y_magnitude = Math.abs(this.start_y - this.stop_y);
x = ((this.start_x - this.stop_x) < 0) ? "RIGHT" : "LEFT";
y = ((this.start_y - this.stop_y) < 0) ? "DOWN" : "UP";
result = (x_magnitude > y_magnitude) ? x : y;
result = (this.tap === true) ? "TAP" : result;
return result;
}
}
}
typeof callback === "string" && konami.load(callback);
if (typeof callback === "function") {
konami.code = callback;
konami.load();
}
return konami;
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = Konami;
} else {
if (typeof define === 'function' && define.amd) {
define([], function() {
return Konami;
});
} else {
window.Konami = Konami;
}
}

169
src/scripts/script.js Normal file
파일 보기

@ -0,0 +1,169 @@
console.log('%c yo! ', 'background: lemonchiffon; border: 1px solid #fff');
console.log('%c welcome to the console <3 ', 'background: lemonchiffon; border: 1px solid #fff');
console.log('%c so uh. what\'cha doin here >w>', 'background: lavenderblush; border: 1px solid #000; padding: 4px; padding-top: 10px; padding-bottom: 8px;');
var $ = require("jquery");
$(function() {
var body = document.querySelector('body');
var light = document.querySelector('.light');
var lightInner = document.querySelector('.light-inner');
var shadow = document.querySelector('.shadow');
var shadowInner = document.querySelector('.shadow-inner');
var settings = {
smoothness: 4,
snapScale: 1.8,
maxSnapScale: 6,
defaultScale: 50 // size of the ball in px (needed for calculations)
}
var state = {
movementSmoothness: 4,
rotation: 0,
currentX: 0,
currentY: 0,
currentScale: 1,
clientX: 0,
clientY: 0,
clientScale: 1,
target: null,
targetScaleMultiplier: 0
};
function move(e) {
if ((e.clientX)) {
// position of the mouse based on the window
var mouseX = (e.clientX);
var mouseY = (e.clientY);
if (state.transition) {
state.clientScale = state.transition;
} else if (state.target) {
state.clientScale = state.target.scale;
} else {
state.clientScale = 1;
}
// get the target position, usualy the mouse position if not snapping
state.clientX = state.target ? state.target.x : (mouseX); // mouse X position or snap target
state.clientY = state.target ? state.target.y : (mouseY); // mouse Y position or snap target
}
}
function moveScroll(e) {
// get the target position, usualy the mouse position if not snapping
state.clientX = state.rawClientX; // mouse X position or snap target
state.clientY = state.rawClientY; // mouse Y position or snap target
}
function snap(e) {
var offset = $(this).offset();
// how far the page has scrolled
var scrollX = window.pageXOffset;
var scrollY = window.pageYOffset;
var scale = Math.min(Math.max(this.offsetWidth, this.offsetHeight) / settings.defaultScale * settings.snapScale, settings.maxSnapScale);
if ($(this).data('snap-scale') !== undefined) {
scale = $(this).data('snap-scale')
}
state.movementSmoothness = settings.smoothness;
state.target = {
x: (offset.left + (this.offsetWidth / 2) - scrollX),
y: (offset.top + (this.offsetHeight / 2) - scrollY),
scale: scale
}
state.clientX = state.target.x;
state.clientY = state.target.y;
state.clientScale = state.target.scale;
}
function unsnap(e) {
state.target = null;
setTimeout(function() {
state.movementSmoothness = 4;
}, 500);
}
setTimeout(function() {
state.transition = 0;
state.clientScale = 0.01;
}, 1500);
var supportsCssVariables = (window.CSS && window.CSS.supports && window.CSS.supports('--fake-var', 0));
function repeatOften() {
// set state
state.currentX = +(state.currentX + (state.clientX - state.currentX) / state.movementSmoothness).toFixed(2);
state.currentY = +(state.currentY + (state.clientY - state.currentY) / state.movementSmoothness).toFixed(2);
//state.currentScale = +(state.currentScale + (state.clientScale - state.currentScale) / settings.smoothness ).toFixed(2);
state.currentScale = state.clientScale;
if (supportsCssVariables) {
// set the css variables
light.style.setProperty('--x', state.currentX + 'px');
light.style.setProperty('--y', state.currentY + 'px');
lightInner.style.setProperty('--scale', (state.currentScale).toFixed(2));
//lightInner.style.setProperty('--scaleY', (state.currentScale).toFixed(2));
shadow.style.setProperty('--x', state.currentX + 'px');
shadow.style.setProperty('--y', state.currentY + 'px');
shadowInner.style.setProperty('--scale', (state.currentScale).toFixed(2));
//shadowInner.style.setProperty('--scaleY', (state.currentScale).toFixed(2));
} else {
light.style.transform = 'translate3d(' + state.currentX + 'px,' + state.currentY + 'px,0)';
lightInner.style.transform = 'scale(' + (state.currentScale).toFixed(2) + ')';
shadow.style.transform = 'translate3d(' + state.currentX + 'px,' + state.currentY + 'px,0)';
shadowInner.style.transform = 'scale(' + (state.currentScale).toFixed(2) + ')';
}
requestAnimationFrame(repeatOften);
}
requestAnimationFrame(repeatOften);
function init() {
$('.light,.shadow').show();
if (window.PointerEvent) {
body.removeEventListener('pointermove', init);
} else {
body.removeEventListener('mousemove', init);
}
}
if (window.PointerEvent) {
body.addEventListener('pointermove', init);
body.addEventListener('pointermove', move);
} else {
body.addEventListener('mousemove', init);
body.addEventListener('mousemove', move);
}
//document.addEventListener("scroll", moveScroll);
$('body').delegate('.snapCursor', 'mouseenter', snap);
$('body').delegate('.snapCursor', 'mouseleave', unsnap);
$('body').delegate('.snapCursor', 'focus', snap);
$('body').delegate('.snapCursor', 'blur', unsnap);
$('.light,.shadow').one('pointermove', function() {
this.style.display = 'block';
})
$('body').bind('unsnap', function(e) {
unsnap();
});
if (window.console && window.console.log) {
//window.console.clear();
window.console.log(' ');
window.console.log('%cyo!', 'color: #040515; font-size:24px;font-family:georgia;font-weight:bold;');
window.console.log(' ');
window.console.log('%chaving fun looking through my code?', 'color: #64656F; font-size:16px;');
window.console.log('%cwarning, it\'s pretty bad.', 'color: #64656F; font-size:8px;');
window.console.log(' ');
}
});

117
webpack.common.js Normal file
파일 보기

@ -0,0 +1,117 @@
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
module: {
rules: [{
test: /\.txt$/,
use: 'raw-loader'
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}]
},
{
test: /\.(jpe?g|png|gif|svg)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
publicPath: 'images/'
}
}]
},
{
test: /\.(woff|woff2|ttf|otf)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
publicPath: 'fonts/'
}
}]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'tris-webpack-boilerplate',
filename: 'index.html',
template: './src/index.html',
inject: 'head'
}),
new PreloadWebpackPlugin({
rel: 'preload',
as(entry) {
if (/\.(woff|woff2|ttf|otf)$/.test(entry)) return 'font';
},
fileWhitelist: [/\.(woff|woff2|ttf|otf)$/],
include: 'allAssets'
}),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'defer'
}),
new MiniCssExtractPlugin({
filename: 'webpack-bundle.css',
chunkFilename: '[id].css'
})
],
externals: {
$: 'jquery',
jquery: 'jQuery',
'window.$': 'jquery',
},
output: {
filename: 'webpack-bundle.js',
path: path.resolve(__dirname, 'dist')
}
};

7
webpack.dev.js Normal file
파일 보기

@ -0,0 +1,7 @@
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map'
});

43
webpack.prod.js Normal file
파일 보기

@ -0,0 +1,43 @@
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const imageminMozjpeg = require('imagemin-mozjpeg');
const CompressionPlugin = require('compression-webpack-plugin');
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
const OfflinePlugin = require('offline-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
optimization: {
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i,
parallel: true,
sourceMap: true,
})
]
},
plugins: [
new CompressionPlugin({
test: /\.(html|css|js)(\?.*)?$/i // only compressed html/css/js, skips compressing sourcemaps etc
}),
new ImageminPlugin({
test: /\.(jpe?g|png|gif|svg)$/i,
gifsicle: { // lossless gif compressor
optimizationLevel: 9
},
pngquant: ({ // lossy png compressor, remove for default lossless
quality: '75'
}),
plugins: [imageminMozjpeg({ // lossy jpg compressor, remove for default lossless
quality: '75'
})]
}),
new OfflinePlugin()
]
});