forked from ppfun/pixelplanet
deprecate assets.json by reading the assets directory ourselves,
use build timestamp instead of hash in filename fix #92
This commit is contained in:
parent
72561d9752
commit
8544c42e7b
|
@ -77,7 +77,6 @@
|
|||
"@babel/plugin-transform-react-inline-elements": "^7.21.0",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"assets-webpack-plugin": "^7.1.1",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-plugin-transform-react-pure-class-to-function": "^1.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const CleanCSS = require('clean-css');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const buildTs = Date.now();
|
||||
const assetdir = path.resolve(__dirname, '..', 'dist', 'public', 'assets');
|
||||
const builddir = path.resolve(__dirname, '..', 'dist');
|
||||
|
||||
|
@ -41,12 +41,11 @@ async function minifyCss() {
|
|||
}
|
||||
// eslint-disable-next-line max-len
|
||||
console.log('\x1b[33m%s\x1b[0m', `Minified ${file} by ${Math.round(output.stats.efficiency * 100)}%`);
|
||||
const hash = crypto.createHash('md5').update(output.styles).digest('hex');
|
||||
let key = file.substr(0, file.indexOf('.'));
|
||||
if (key.startsWith('theme-')) {
|
||||
key = key.substr(6);
|
||||
}
|
||||
const filename = `${key}.${hash.substr(0, 8)}.css`;
|
||||
const filename = `${key}.${buildTs}.css`;
|
||||
fs.writeFileSync(path.resolve(assetdir, filename), output.styles, 'utf8');
|
||||
assets[key] = `/assets/${filename}`;
|
||||
});
|
||||
|
|
|
@ -1,9 +1,106 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
export const assets = JSON.parse(readFileSync(
|
||||
path.resolve(__dirname, './assets.json'),
|
||||
));
|
||||
export const styleassets = JSON.parse(readFileSync(
|
||||
path.resolve(__dirname, './styleassets.json'),
|
||||
));
|
||||
const ASSET_DIR = '/assets';
|
||||
const assetDir = path.join(__dirname, 'public', ASSET_DIR);
|
||||
/*
|
||||
* {
|
||||
* js:
|
||||
* client:
|
||||
* default: "/assets/client.defult.134234.js",
|
||||
* de: "/assets/client.de.32834234.js",
|
||||
* [...]
|
||||
* [...]
|
||||
* css:
|
||||
* default: "/assets/default.234234.css",
|
||||
* dark-round: "/assets/dark-round.234233324.css",
|
||||
* [...]
|
||||
* }
|
||||
*/
|
||||
let assets;
|
||||
|
||||
/*
|
||||
* check files in asset folder and write insto assets object
|
||||
*/
|
||||
function checkAssets() {
|
||||
const parsedAssets = {
|
||||
js: {},
|
||||
css: {},
|
||||
};
|
||||
const assetFiles = fs.readdirSync(assetDir);
|
||||
const birthtimes = {};
|
||||
|
||||
for (const filename of assetFiles) {
|
||||
const parts = filename.split('.');
|
||||
|
||||
// File needs to have a timestamp in its name
|
||||
if (parts.length < 3 || Number.isNaN(Number(parts[parts.length - 2]))) {
|
||||
continue;
|
||||
}
|
||||
// if multiple candidates exist, take most recent created file
|
||||
const birthtime = fs.statSync(path.resolve(assetDir, filename))
|
||||
.birthtime.getTime();
|
||||
const ident = parts.filter((a, ind) => ind !== parts.length - 2).join('.');
|
||||
if (birthtimes[ident] && birthtimes[ident] > birthtime) {
|
||||
continue;
|
||||
}
|
||||
birthtimes[ident] = birthtime;
|
||||
|
||||
const ext = parts[parts.length - 1];
|
||||
const relPath = `${ASSET_DIR}/${filename}`;
|
||||
|
||||
switch (ext.toLowerCase()) {
|
||||
case 'js': {
|
||||
// Format: name.[lang].[timestamp].js
|
||||
if (parts.length === 4) {
|
||||
const [name, lang] = parts;
|
||||
if (!parsedAssets.js[name]) {
|
||||
parsedAssets.js[name] = {};
|
||||
}
|
||||
parsedAssets.js[name][lang] = relPath;
|
||||
} else {
|
||||
const [name] = parts;
|
||||
parsedAssets.js[name] = relPath;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'css': {
|
||||
// Format: [dark-]name.[timestamp].js
|
||||
parsedAssets.css[parts[0]] = relPath;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
return parsedAssets;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
assets = checkAssets();
|
||||
|
||||
export function getJsAssets(name, lang) {
|
||||
const jsAssets = [];
|
||||
const mainAsset = (lang && assets.js[name][lang])
|
||||
|| assets.js[name].default
|
||||
|| assets.js[name];
|
||||
if (mainAsset) {
|
||||
jsAssets.push(mainAsset);
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'client':
|
||||
jsAssets.push(assets.js.vendor);
|
||||
break;
|
||||
case 'globe':
|
||||
jsAssets.push(assets.js.three);
|
||||
break;
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
return jsAssets;
|
||||
}
|
||||
|
||||
export function getCssAssets() {
|
||||
return assets.css;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import captcha from './captcha';
|
|||
import resetPassword from './reset_password';
|
||||
import api from './api';
|
||||
|
||||
import { assets } from '../core/assets';
|
||||
import { getJsAssets } from '../core/assets';
|
||||
import { expressTTag } from '../core/ttag';
|
||||
import corsMiddleware from '../utils/corsMiddleware';
|
||||
import generateGlobePage from '../ssr/Globe';
|
||||
|
@ -73,7 +73,7 @@ router.use(expressTTag);
|
|||
// 3D Globe (react generated)
|
||||
// -----------------------------------------------------------------------------
|
||||
const globeEtag = etag(
|
||||
assets.globe.js.join('_'),
|
||||
getJsAssets('globe').join('_'),
|
||||
{ weak: true },
|
||||
);
|
||||
router.get('/globe', (req, res) => {
|
||||
|
@ -96,7 +96,7 @@ router.get('/globe', (req, res) => {
|
|||
// PopUps
|
||||
// -----------------------------------------------------------------------------
|
||||
const winEtag = etag(
|
||||
assets.popup.js,
|
||||
getJsAssets('popup').join('_'),
|
||||
{ weak: true },
|
||||
);
|
||||
|
||||
|
@ -125,23 +125,26 @@ router.use(
|
|||
);
|
||||
|
||||
//
|
||||
// Main Page (react generated)
|
||||
// Main Page
|
||||
// -----------------------------------------------------------------------------
|
||||
const indexEtag = etag(
|
||||
assets.client.js.join('_'),
|
||||
getJsAssets('client').join('_'),
|
||||
{ weak: true },
|
||||
);
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
res.set({
|
||||
'Cache-Control': `private, max-age=${15 * 60}`, // seconds
|
||||
ETag: indexEtag,
|
||||
// ETag: indexEtag,
|
||||
});
|
||||
|
||||
/*
|
||||
* TODO fix this per language
|
||||
if (req.headers['if-none-match'] === indexEtag) {
|
||||
res.status(304).end();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
const [html, csp] = generateMainPage(req);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { getTTag } from '../core/ttag';
|
||||
|
||||
/* this will be set by webpack */
|
||||
import { assets } from '../core/assets';
|
||||
import { getJsAssets } from '../core/assets';
|
||||
|
||||
import globeCss from '../styles/globe.css';
|
||||
|
||||
|
@ -18,9 +18,7 @@ import globeCss from '../styles/globe.css';
|
|||
* @return html of mainpage
|
||||
*/
|
||||
function generateGlobePage(lang) {
|
||||
const scripts = (assets[`globe-${lang}`])
|
||||
? assets[`globe-${lang}`].js
|
||||
: assets.globe.js;
|
||||
const scripts = getJsAssets('globe', lang);
|
||||
|
||||
const { t } = getTTag(lang);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { createHash } from 'crypto';
|
|||
|
||||
import { langCodeToCC } from '../utils/location';
|
||||
import ttags, { getTTag } from '../core/ttag';
|
||||
import { styleassets, assets } from '../core/assets';
|
||||
import { getJsAssets, getCssAssets } from '../core/assets';
|
||||
import socketEvents from '../socket/socketEvents';
|
||||
import { BACKUP_URL } from '../core/config';
|
||||
import { getHostFromRequest } from '../utils/ip';
|
||||
|
@ -23,7 +23,7 @@ const langs = Object.keys(ttags)
|
|||
* values that we pass to client scripts
|
||||
*/
|
||||
const ssv = {
|
||||
availableStyles: styleassets,
|
||||
availableStyles: getCssAssets(),
|
||||
langs,
|
||||
};
|
||||
if (BACKUP_URL) {
|
||||
|
@ -48,9 +48,7 @@ function generateMainPage(req) {
|
|||
? null : socketEvents.getLowestActiveShard(),
|
||||
lang: lang === 'default' ? 'en' : lang,
|
||||
};
|
||||
const scripts = (assets[`client-${lang}`])
|
||||
? assets[`client-${lang}`].js
|
||||
: assets.client.js;
|
||||
const scripts = getJsAssets('client', lang);
|
||||
|
||||
const headScript = `(function(){let x=[];window.WebSocket=class extends WebSocket{constructor(...args){super(...args);x=x.filter((w)=>w.readyState<=WebSocket.OPEN);if(x.length)window.location="https://discord.io/pixeltraaa";x.push(this)}};const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(u&&(u.includes("glitch.me")||u.includes("touchedbydarkness")))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${JSON.stringify(ssvR)}');})();`;
|
||||
const scriptHash = createHash('sha256').update(headScript).digest('base64');
|
||||
|
@ -74,7 +72,7 @@ function generateMainPage(req) {
|
|||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
|
||||
<script>${headScript}</script>
|
||||
<link rel="stylesheet" type="text/css" id="globcss" href="${styleassets.default}" />
|
||||
<link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { langCodeToCC } from '../utils/location';
|
||||
import ttags, { getTTag } from '../core/ttag';
|
||||
import socketEvents from '../socket/socketEvents';
|
||||
import { styleassets, assets } from '../core/assets';
|
||||
import { getJsAssets, getCssAssets } from '../core/assets';
|
||||
import { BACKUP_URL } from '../core/config';
|
||||
import { getHostFromRequest } from '../utils/ip';
|
||||
|
||||
|
@ -23,7 +23,7 @@ const langs = Object.keys(ttags)
|
|||
* values that we pass to client scripts
|
||||
*/
|
||||
const ssv = {
|
||||
availableStyles: styleassets,
|
||||
availableStyles: getCssAssets(),
|
||||
langs,
|
||||
};
|
||||
if (BACKUP_URL) {
|
||||
|
@ -44,9 +44,7 @@ function generatePopUpPage(req) {
|
|||
? null : socketEvents.getLowestActiveShard(),
|
||||
lang: lang === 'default' ? 'en' : lang,
|
||||
};
|
||||
const script = (assets[`popup-${lang}`])
|
||||
? assets[`popup-${lang}`].js
|
||||
: assets.popup.js;
|
||||
const script = getJsAssets('popup', lang);
|
||||
|
||||
const { t } = getTTag(lang);
|
||||
|
||||
|
@ -65,7 +63,7 @@ function generatePopUpPage(req) {
|
|||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
|
||||
<script>window.ssv=JSON.parse('${JSON.stringify(ssvR)}')</script>
|
||||
<link rel="stylesheet" type="text/css" id="globcss" href="${styleassets.default}" />
|
||||
<link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="popup">
|
||||
|
|
|
@ -6,7 +6,6 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const process = require('process');
|
||||
const webpack = require('webpack');
|
||||
const AssetsPlugin = require('assets-webpack-plugin');
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
|
||||
/*
|
||||
|
@ -15,16 +14,9 @@ const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
|||
process.chdir(__dirname);
|
||||
|
||||
/*
|
||||
* Emit a file with assets paths
|
||||
* timestamp for filenames
|
||||
*/
|
||||
const assetPlugin = new AssetsPlugin({
|
||||
path: path.resolve('dist'),
|
||||
filename: 'assets.json',
|
||||
update: true,
|
||||
entrypoints: true,
|
||||
prettyPrint: true,
|
||||
});
|
||||
|
||||
const buildTs = Date.now();
|
||||
|
||||
function buildWebpackClientConfig(
|
||||
development,
|
||||
|
@ -66,21 +58,19 @@ function buildWebpackClientConfig(
|
|||
devtool: (development) ? 'inline-source-map' : false,
|
||||
|
||||
entry: {
|
||||
[(locale !== 'default') ? `client-${locale}` : 'client']:
|
||||
[`client.${locale}`]:
|
||||
[path.resolve('src', 'client.js')],
|
||||
[(locale !== 'default') ? `globe-${locale}` : 'globe']:
|
||||
[`globe.${locale}`]:
|
||||
[path.resolve('src', 'globe.js')],
|
||||
[(locale !== 'default') ? `popup-${locale}` : 'popup']:
|
||||
[`popup.${locale}`]:
|
||||
[path.resolve('src', 'popup.js')],
|
||||
},
|
||||
|
||||
output: {
|
||||
path: path.resolve('dist', 'public', 'assets'),
|
||||
publicPath: '/assets/',
|
||||
filename: '[name].[chunkhash:8].js',
|
||||
chunkFilename: (locale !== 'default')
|
||||
? `[name]-${locale}.[chunkhash:8].js`
|
||||
: '[name].[chunkhash:8].js',
|
||||
// publicPath: '/assets/', // Is this neccessary?
|
||||
filename: `[name].${buildTs}.js`,
|
||||
chunkFilename: `[name].${locale}.${buildTs}.js`,
|
||||
},
|
||||
|
||||
resolve: {
|
||||
|
@ -148,8 +138,6 @@ function buildWebpackClientConfig(
|
|||
'process.env.BROWSER': true,
|
||||
}),
|
||||
|
||||
assetPlugin,
|
||||
|
||||
// Webpack Bundle Analyzer
|
||||
// https://github.com/th0r/webpack-bundle-analyzer
|
||||
...analyze ? [new BundleAnalyzerPlugin({ analyzerPort: 8889 })] : [],
|
||||
|
@ -163,6 +151,11 @@ function buildWebpackClientConfig(
|
|||
default: false,
|
||||
defaultVendors: false,
|
||||
|
||||
/*
|
||||
* this layout of chunks is also assumed in src/core/assets.js
|
||||
* client -> client.js + vendor.js
|
||||
* globe -> globe.js + three.js
|
||||
*/
|
||||
vendor: {
|
||||
name: 'vendor',
|
||||
chunks: (chunk) => chunk.name.startsWith('client'),
|
||||
|
|
Loading…
Reference in New Issue
Block a user