mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-15 18:19:47 +00:00
Compare commits
1 Commits
readme-typ
...
analytics
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce4010e52b |
31
README.md
31
README.md
@@ -1,32 +1,5 @@
|
|||||||
# Squoosh!
|
# Squoosh!
|
||||||
|
|
||||||
Squoosh is an image compression web app that allows you to dive into the advanced options provided
|
Squoosh will be an image compression web app that allows you to dive into the
|
||||||
by various image compressors.
|
advanced options provided by various image compressors.
|
||||||
|
|
||||||
# Privacy
|
|
||||||
|
|
||||||
Google Analytics is used to record the following:
|
|
||||||
|
|
||||||
* [Basic visit data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631).
|
|
||||||
* Before and after image size once an image is downloaded. These values are rounded to the nearest
|
|
||||||
kilobyte.
|
|
||||||
|
|
||||||
Image compression is handled locally; no additional data is sent to the server.
|
|
||||||
|
|
||||||
# Building locally
|
|
||||||
|
|
||||||
Clone the repo, and:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll get an error on first build because of [a stupid bug we haven't fixed
|
|
||||||
yet](https://github.com/GoogleChromeLabs/squoosh/issues/251).
|
|
||||||
|
|
||||||
You can run the development server with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|||||||
18
_headers.ejs
18
_headers.ejs
@@ -1,18 +0,0 @@
|
|||||||
# Long-term cache by default.
|
|
||||||
/*
|
|
||||||
Cache-Control: max-age=31536000
|
|
||||||
|
|
||||||
# And here are the exceptions:
|
|
||||||
/
|
|
||||||
Cache-Control: no-cache
|
|
||||||
|
|
||||||
/serviceworker.js
|
|
||||||
Cache-Control: no-cache
|
|
||||||
|
|
||||||
/manifest.json
|
|
||||||
Cache-Control: must-revalidate, max-age=3600
|
|
||||||
|
|
||||||
# URLs in /assets do not include a hash and are mutable.
|
|
||||||
# But it isn't a big deal if the user gets an old version.
|
|
||||||
/assets/*
|
|
||||||
Cache-Control: must-revalidate, max-age=3600
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const ejs = require('ejs');
|
|
||||||
const AssetsPlugin = require('assets-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = class AssetTemplatePlugin extends AssetsPlugin {
|
|
||||||
constructor(options) {
|
|
||||||
options = options || {};
|
|
||||||
if (!options.template) throw Error('AssetTemplatePlugin: template option is required.');
|
|
||||||
super({
|
|
||||||
useCompilerPath: true,
|
|
||||||
filename: options.filename,
|
|
||||||
processOutput: files => this._processOutput(files)
|
|
||||||
});
|
|
||||||
this._template = path.resolve(process.cwd(), options.template);
|
|
||||||
const ignore = options.ignore || /(manifest\.json|\.DS_Store)$/;
|
|
||||||
this._ignore = typeof ignore === 'function' ? ({ test: ignore }) : ignore;
|
|
||||||
}
|
|
||||||
|
|
||||||
_processOutput(files) {
|
|
||||||
const mapping = {
|
|
||||||
all: [],
|
|
||||||
byType: {},
|
|
||||||
entries: {}
|
|
||||||
};
|
|
||||||
for (const entryName in files) {
|
|
||||||
// non-entry-point-derived assets are collected under an empty string key
|
|
||||||
// since that's a bit awkward, we'll call them "assets"
|
|
||||||
const name = entryName === '' ? 'assets' : entryName;
|
|
||||||
const listing = files[entryName];
|
|
||||||
const entry = mapping.entries[name] = {
|
|
||||||
all: [],
|
|
||||||
byType: {}
|
|
||||||
};
|
|
||||||
for (let type in listing) {
|
|
||||||
const list = [].concat(listing[type]).filter(file => !this._ignore.test(file));
|
|
||||||
if (!list.length) continue;
|
|
||||||
mapping.all = mapping.all.concat(list);
|
|
||||||
mapping.byType[type] = (mapping.byType[type] || []).concat(list);
|
|
||||||
entry.all = entry.all.concat(list);
|
|
||||||
entry.byType[type] = (entry.byType[type] || []).concat(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapping.files = mapping.all;
|
|
||||||
return ejs.render(fs.readFileSync(this._template, 'utf8'), mapping);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
29
package-lock.json
generated
29
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "squoosh",
|
"name": "squoosh",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -578,27 +578,6 @@
|
|||||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
|
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"assets-webpack-plugin": {
|
|
||||||
"version": "3.9.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/assets-webpack-plugin/-/assets-webpack-plugin-3.9.7.tgz",
|
|
||||||
"integrity": "sha512-yxo4MlSb++B88qQFE27Wf56ykGaDHZeKcSbrstSFOOwOxv33gWXtM49+yfYPSErlXPAMT5lVy3YPIhWlIFjYQw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"camelcase": "^5.0.0",
|
|
||||||
"escape-string-regexp": "^1.0.3",
|
|
||||||
"lodash.assign": "^4.2.0",
|
|
||||||
"lodash.merge": "^4.6.1",
|
|
||||||
"mkdirp": "^0.5.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"camelcase": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assign-symbols": {
|
"assign-symbols": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
||||||
@@ -7944,12 +7923,6 @@
|
|||||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lodash.merge": {
|
|
||||||
"version": "4.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
|
|
||||||
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"lodash.mergewith": {
|
"lodash.mergewith": {
|
||||||
"version": "4.6.1",
|
"version": "4.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "squoosh",
|
"name": "squoosh",
|
||||||
"version": "1.0.2",
|
"version": "0.1.0",
|
||||||
"license": "apache-2.0",
|
"license": "apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack serve --host 0.0.0.0 --hot",
|
"start": "webpack serve --host 0.0.0.0 --hot",
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
"@types/pretty-bytes": "^5.1.0",
|
"@types/pretty-bytes": "^5.1.0",
|
||||||
"@types/webassembly-js-api": "0.0.1",
|
"@types/webassembly-js-api": "0.0.1",
|
||||||
"@webcomponents/custom-elements": "^1.2.1",
|
"@webcomponents/custom-elements": "^1.2.1",
|
||||||
"assets-webpack-plugin": "^3.9.7",
|
|
||||||
"babel-loader": "^7.1.5",
|
"babel-loader": "^7.1.5",
|
||||||
"babel-plugin-jsx-pragmatic": "^1.0.2",
|
"babel-plugin-jsx-pragmatic": "^1.0.2",
|
||||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||||
@@ -37,7 +36,6 @@
|
|||||||
"copy-webpack-plugin": "^4.5.3",
|
"copy-webpack-plugin": "^4.5.3",
|
||||||
"critters-webpack-plugin": "^2.0.1",
|
"critters-webpack-plugin": "^2.0.1",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"ejs": "^2.6.1",
|
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"file-drop-element": "^0.0.9",
|
"file-drop-element": "^0.0.9",
|
||||||
"file-loader": "^1.1.11",
|
"file-loader": "^1.1.11",
|
||||||
|
|||||||
@@ -48,15 +48,6 @@ export default class Output extends Component<Props, State> {
|
|||||||
const leftDraw = this.leftDrawable();
|
const leftDraw = this.leftDrawable();
|
||||||
const rightDraw = this.rightDrawable();
|
const rightDraw = this.rightDrawable();
|
||||||
|
|
||||||
// Reset the pinch zoom, which may have an position set from the previous view, after pressing
|
|
||||||
// the back button.
|
|
||||||
this.pinchZoomLeft!.setTransform({
|
|
||||||
allowChangeEvent: true,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
scale: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.canvasLeft && leftDraw) {
|
if (this.canvasLeft && leftDraw) {
|
||||||
drawDataToCanvas(this.canvasLeft, leftDraw);
|
drawDataToCanvas(this.canvasLeft, leftDraw);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,11 +129,6 @@ export default class Intro extends Component<Props, State> {
|
|||||||
<ul class={style.relatedLinks}>
|
<ul class={style.relatedLinks}>
|
||||||
<li><a href="https://github.com/GoogleChromeLabs/squoosh/">View the code</a></li>
|
<li><a href="https://github.com/GoogleChromeLabs/squoosh/">View the code</a></li>
|
||||||
<li><a href="https://github.com/GoogleChromeLabs/squoosh/issues">Report a bug</a></li>
|
<li><a href="https://github.com/GoogleChromeLabs/squoosh/issues">Report a bug</a></li>
|
||||||
<li>
|
|
||||||
<a href="https://github.com/GoogleChromeLabs/squoosh/blob/master/README.md#privacy">
|
|
||||||
Privacy
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -60,16 +60,11 @@ export default class Results extends Component<Props, State> {
|
|||||||
|
|
||||||
@bind
|
@bind
|
||||||
onDownload() {
|
onDownload() {
|
||||||
// GA can’t do floats. So we round to ints. We're deliberately rounding to nearest kilobyte to
|
|
||||||
// avoid cases where exact image sizes leak something interesting about the user.
|
|
||||||
const before = Math.round(this.props.source!.file.size / 1024);
|
|
||||||
const after = Math.round(this.props.imageFile!.size / 1024);
|
|
||||||
const change = Math.round(after / before * 1000);
|
|
||||||
|
|
||||||
ga('send', 'event', 'compression', 'download', {
|
ga('send', 'event', 'compression', 'download', {
|
||||||
metric1: before,
|
// GA can’t do floats. So we round to ints.
|
||||||
metric2: after,
|
metric1: Math.floor(this.props.source!.file.size),
|
||||||
metric3: change,
|
metric2: Math.floor(this.props.imageFile!.size),
|
||||||
|
metric3: Math.floor(this.props.imageFile!.size / this.props.source!.file.size * 1000),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ if (!('customElements' in self)) {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof PRERENDER === 'undefined') {
|
|
||||||
window.ga = window.ga || ((...args) => (ga.q = ga.q || []).push(args));
|
window.ga = window.ga || ((...args) => (ga.q = ga.q || []).push(args));
|
||||||
ga('create', 'UA-128752250-1', 'auto');
|
ga('create', 'UA-128752250-1', 'auto');
|
||||||
ga('set', 'transport', 'beacon');
|
ga('set', 'transport', 'beacon');
|
||||||
@@ -22,4 +21,3 @@ if (typeof PRERENDER === 'undefined') {
|
|||||||
const s = document.createElement('script');
|
const s = document.createElement('script');
|
||||||
s.src = 'https://www.google-analytics.com/analytics.js';
|
s.src = 'https://www.google-analytics.com/analytics.js';
|
||||||
document.head!.appendChild(s);
|
document.head!.appendChild(s);
|
||||||
}
|
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ async function updateReady(reg: ServiceWorkerRegistration): Promise<void> {
|
|||||||
|
|
||||||
/** Set up the service worker and monitor changes */
|
/** Set up the service worker and monitor changes */
|
||||||
export async function offliner(showSnack: SnackBarElement['showSnackbar']) {
|
export async function offliner(showSnack: SnackBarElement['showSnackbar']) {
|
||||||
// This needs to be a typeof because Webpack.
|
|
||||||
if (typeof PRERENDER === 'boolean') return;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
navigator.serviceWorker.register('../sw');
|
navigator.serviceWorker.register('../sw');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export async function cacheBasics(cacheName: string, buildAssets: string[]) {
|
|||||||
const toCache = ['/', '/assets/favicon.ico'];
|
const toCache = ['/', '/assets/favicon.ico'];
|
||||||
|
|
||||||
const prefixesToCache = [
|
const prefixesToCache = [
|
||||||
|
// First interaction JS & CSS:
|
||||||
|
'first-interaction.',
|
||||||
// Main app JS & CSS:
|
// Main app JS & CSS:
|
||||||
'main-app.',
|
'main-app.',
|
||||||
// Service worker handler:
|
// Service worker handler:
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
|
|||||||
const WorkerPlugin = require('worker-plugin');
|
const WorkerPlugin = require('worker-plugin');
|
||||||
const AutoSWPlugin = require('./config/auto-sw-plugin');
|
const AutoSWPlugin = require('./config/auto-sw-plugin');
|
||||||
const CrittersPlugin = require('critters-webpack-plugin');
|
const CrittersPlugin = require('critters-webpack-plugin');
|
||||||
const AssetTemplatePlugin = require('./config/asset-template-plugin');
|
|
||||||
|
|
||||||
function readJson (filename) {
|
function readJson (filename) {
|
||||||
return JSON.parse(fs.readFileSync(filename));
|
return JSON.parse(fs.readFileSync(filename));
|
||||||
@@ -226,7 +225,7 @@ module.exports = function (_, env) {
|
|||||||
// For now we're not doing SSR.
|
// For now we're not doing SSR.
|
||||||
new HtmlPlugin({
|
new HtmlPlugin({
|
||||||
filename: path.join(__dirname, 'build/index.html'),
|
filename: path.join(__dirname, 'build/index.html'),
|
||||||
template: isProd ? '!!prerender-loader?string!src/index.html' : 'src/index.html',
|
template: '!!prerender-loader?string!src/index.html',
|
||||||
minify: isProd && {
|
minify: isProd && {
|
||||||
collapseWhitespace: true,
|
collapseWhitespace: true,
|
||||||
removeScriptTypeAttributes: true,
|
removeScriptTypeAttributes: true,
|
||||||
@@ -239,11 +238,8 @@ module.exports = function (_, env) {
|
|||||||
compile: true
|
compile: true
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new AutoSWPlugin({ version: VERSION }),
|
new AutoSWPlugin({
|
||||||
|
version: VERSION
|
||||||
isProd && new AssetTemplatePlugin({
|
|
||||||
template: path.join(__dirname, '_headers.ejs'),
|
|
||||||
filename: '_headers',
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new ScriptExtHtmlPlugin({
|
new ScriptExtHtmlPlugin({
|
||||||
|
|||||||
Reference in New Issue
Block a user