change how we build stuff

This commit is contained in:
HF 2021-01-29 22:46:58 +01:00
parent 6367e2926f
commit 840a172816
21 changed files with 671 additions and 508 deletions

View File

@ -150,7 +150,7 @@ The default configuration values can be seen in `src/core/config.js` and for the
#### Styles
To add more css styles, create a new css file in `src/styles` based on `src/styles/default.css` and add it to the FILES array in `tools/minifyCss.js`
To add more css styles, create a new css file in `src/styles` based on `src/styles/default.css` with a filename beginning with "theme-" and rebuild`
### Running

15
i18n/ssr-de.po Normal file
View File

@ -0,0 +1,15 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals = 2; plural = (n != 1);\n"
"Language: de\n"
"mime-version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/ssr-components/Globe.jsx:23
msgid "Double click on globe to go back."
msgstr "Doppelklick um zurück zu gehen"
#: src/ssr-components/Globe.jsx:24
msgid "Loading..."
msgstr "Lade..."

12
i18n/template-ssr.pot Normal file
View File

@ -0,0 +1,12 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: src/ssr-components/Globe.jsx:46
msgid "Double click on globe to go back."
msgstr ""
#: src/ssr-components/Globe.jsx:47
msgid "Loading..."
msgstr ""

343
i18n/template.pot Normal file
View File

@ -0,0 +1,343 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: src/components/ExpandMenuButton.jsx:19
msgid "Close Menu"
msgstr ""
#: src/components/ExpandMenuButton.jsx:19
msgid "Open Menu"
msgstr ""
#: src/components/OnlineBox.jsx:22
msgid "User online"
msgstr ""
#: src/components/OnlineBox.jsx:25
msgid "Pixel gesetzt"
msgstr ""
#: src/components/ChatButton.jsx:61
msgid "Close Chat"
msgstr ""
#: src/components/ChatButton.jsx:61
msgid "Open Chat"
msgstr ""
#: src/components/CanvasSwitchButton.jsx:20
msgid "Canvas Selection"
msgstr ""
#: src/components/CoordinatesBox.jsx:26
msgid "Copy to Clipboard"
msgstr ""
#: src/components/DownloadButton.jsx:37
msgid "Make Screenshot"
msgstr ""
#: src/components/SettingsButton.jsx:20
msgid "Settings"
msgstr ""
#: src/components/LogInButton.jsx:20
msgid "User Area"
msgstr ""
#: src/components/HelpButton.jsx:20
msgid "Help"
msgstr ""
#: src/components/Chat.jsx:143
msgid "Channel settings"
msgstr ""
#: src/components/Chat.jsx:152
msgid "maximize"
msgstr ""
#: src/components/Chat.jsx:168
msgid "Start chatting here"
msgstr ""
#: src/components/Chat.jsx:200
msgid "Chat here"
msgstr ""
#: src/components/Chat.jsx:219
msgid "You must be logged in to chat"
msgstr ""
#: src/components/GlobeButton.jsx:31
msgid "Globe View"
msgstr ""
#: src/components/HelpModal.jsx:34
#: src/components/HelpModal.jsx:35
msgid "Privacy Policy"
msgstr ""
#: src/components/HelpModal.jsx:36
#: src/components/HelpModal.jsx:37
msgid "Terms of Service"
msgstr ""
#: src/components/HelpModal.jsx:40
msgid "your IP"
msgstr ""
#: src/components/HelpModal.jsx:46
msgid "Place color pixels on a large canvas with other players online!"
msgstr ""
#: src/components/HelpModal.jsx:47
msgid ""
"Our main canvas is a huge worldmap, you can place wherever you like, but "
"you will have to wait a specific Cooldown between pixels. You can check out "
"the cooldown and requiremnts on the Canvas Selection menu (globe button on "
"top). Some canvases have a different cooldown for replacing a user-set "
"pixels than placing on a unset pixel. i.e. 4s/7s means 4s on fresh pixels "
"and 7s on already set pixels."
msgstr ""
#: src/components/HelpModal.jsx:51
msgid ""
"Higher zoomlevels take some time to update, the 3D globe gets updated at "
"least once per day."
msgstr ""
#: src/components/HelpModal.jsx:52
msgid "Have fun!"
msgstr ""
#: src/components/HelpModal.jsx:54
msgid "recommended"
msgstr ""
#: src/components/HelpModal.jsx:55
msgid "Source on "
msgstr ""
#: src/components/HelpModal.jsx:56
msgid "Map Data"
msgstr ""
#: src/components/HelpModal.jsx:57
msgid ""
"The bare map data that we use, together with converted OpenStreetMap tiles "
"for orientation, can be downloaded from mega.nz here: "
msgstr ""
#: src/components/HelpModal.jsx:59
msgid "Detected as Proxy?"
msgstr ""
#: src/components/HelpModal.jsx:61
#, javascript-format
msgid ""
"If you got detected as proxy, but you are none, please go to our ${ "
"guildedLink } or send us an e-mail with ${ getIPLink } to ${ mailLink }. Do "
"not post your IP anywhere else. We are sorry for the inconvenience."
msgstr ""
#: src/components/HelpModal.jsx:63
#: src/components/HelpModal.jsx:80
msgid "Controls"
msgstr ""
#: src/components/HelpModal.jsx:65
msgid "Click a color in palette to select it"
msgstr ""
#: src/components/HelpModal.jsx:66
#, javascript-format
msgid "Press ${ bindG } to toggle grid"
msgstr ""
#: src/components/HelpModal.jsx:67
msgid "Press ${ bindX } to toggle showing of pixel activity"
msgstr ""
#: src/components/HelpModal.jsx:68
#, javascript-format
msgid "Press ${ bindH } to toggle historical view"
msgstr ""
#: src/components/HelpModal.jsx:69
msgid "Press ${ bindR } to copy coordinates"
msgstr ""
#: src/components/HelpModal.jsx:70
#, javascript-format
msgid "Press ${ bindQ } or ${ bindE } to zoom"
msgstr ""
#: src/components/HelpModal.jsx:71
#: src/components/HelpModal.jsx:82
#, javascript-format
msgid "Press ${ bindW }, ${ bindA }, ${ bindS }, ${ bindD } to move"
msgstr ""
#: src/components/HelpModal.jsx:72
#: src/components/HelpModal.jsx:83
#, javascript-format
msgid "Press ${ bindAUp }, ${ bindALeft }, ${ bindADown }, ${ bindARight } to move"
msgstr ""
#: src/components/HelpModal.jsx:73
msgid "Drag ${ mouseSymbol } mouse or ${ touchSymbol } pan to move"
msgstr ""
#: src/components/HelpModal.jsx:74
#, javascript-format
msgid "Scroll ${ mouseSymbol } mouse wheel or ${ touchSymbol } pinch to zoom"
msgstr ""
#: src/components/HelpModal.jsx:75
msgid "Hold left ${ bindShift } for placing while moving mouse"
msgstr ""
#: src/components/HelpModal.jsx:76
#, javascript-format
msgid ""
"Hold right ${ bindShift } for placing while moving mouse according to "
"historical view"
msgstr ""
#: src/components/HelpModal.jsx:77
#: src/components/HelpModal.jsx:88
#, javascript-format
msgid "${ mouseSymbol } Left click or ${ touchSymbol } tap to place a pixel"
msgstr ""
#: src/components/HelpModal.jsx:78
#: src/components/HelpModal.jsx:90
msgid ""
"Click ${ mouseSymbol } middle mouse button or ${ touchSymbol } long-tap to "
"select current hovering color"
msgstr ""
#: src/components/HelpModal.jsx:84
msgid "Press ${ bindE } and ${ bindC } to fly up and down"
msgstr ""
#: src/components/HelpModal.jsx:85
#, javascript-format
msgid "${ mouseSymbol } Hold left mouse button and drag mouse to rotate"
msgstr ""
#: src/components/HelpModal.jsx:86
#, javascript-format
msgid ""
"${ mouseSymbol } Scroll mouse wheel or hold ${ mouseSymbol } middle mouse "
"button and drag to zoom"
msgstr ""
#: src/components/HelpModal.jsx:87
msgid "${ mouseSymbol } Right click and drag mouse to pan"
msgstr ""
#: src/components/HelpModal.jsx:89
#, javascript-format
msgid ""
"${ mouseSymbol } Right click or ${ touchSymbol } double-tap to remove a "
"pixel"
msgstr ""
#: src/components/HelpModal.jsx:92
msgid "Partners:"
msgstr ""
#: src/components/HelpModal.jsx:97
#, javascript-format
msgid ""
"This site is protected by reCAPTCHA and the Google ${ reCaptchaPP } and ${ "
"reCaptchaTOS } apply."
msgstr ""
#: src/components/HelpModal.jsx:103
msgid ""
"This site is protected by hCAPTCHA and its ${ hCaptchaPP } and ${ "
"hCaptchaTOS } apply."
msgstr ""
#: src/components/HelpModal.jsx:113
msgid "Welcome to PixelPlanet.fun"
msgstr ""
#: src/components/PalselButton.jsx:25
msgid "Close Palette"
msgstr ""
#: src/components/PalselButton.jsx:25
msgid "Open Palette"
msgstr ""
#: src/components/ForgotPasswordModal.jsx:20
#: src/components/RegisterModal.jsx:21
#: src/components/UserAreaModal.jsx:128
msgid "Consider joining us on Guilded:"
msgstr ""
#: src/components/HelpModal.jsx:15
msgctxt "keybinds"
msgid "G"
msgstr ""
#: src/components/HelpModal.jsx:16
msgctxt "keybinds"
msgid "X"
msgstr ""
#: src/components/HelpModal.jsx:17
msgctxt "keybinds"
msgid "H"
msgstr ""
#: src/components/HelpModal.jsx:18
msgctxt "keybinds"
msgid "R"
msgstr ""
#: src/components/HelpModal.jsx:19
msgctxt "keybinds"
msgid "Q"
msgstr ""
#: src/components/HelpModal.jsx:20
msgctxt "keybinds"
msgid "E"
msgstr ""
#: src/components/HelpModal.jsx:21
msgctxt "keybinds"
msgid "W"
msgstr ""
#: src/components/HelpModal.jsx:22
msgctxt "keybinds"
msgid "A"
msgstr ""
#: src/components/HelpModal.jsx:23
msgctxt "keybinds"
msgid "S"
msgstr ""
#: src/components/HelpModal.jsx:24
msgctxt "keybinds"
msgid "D"
msgstr ""
#: src/components/HelpModal.jsx:31
msgctxt "keybinds"
msgid "Shift"
msgstr ""
#: src/components/HelpModal.jsx:32
msgctxt "keybinds"
msgid "C"
msgstr ""

476
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"description": "Unlimited planet canvas for placing pixels",
"main": "server.js",
"scripts": {
"build": "babel-node scripts/run build",
"build": "babel-node scripts/run prebuild && npm run webpack",
"clean": "babel-node scripts/run clean",
"webpack": "webpack --config webpack.config.web.babel.js && webpack --config webpack.config.client.babel.js",
"babel-node": "cd $INIT_CWD && babel-node",
@ -78,6 +78,7 @@
"three": "^0.123.0",
"three-trackballcontrols": "^0.9.0",
"ttag": "^1.7.24",
"ttag-po-loader": "0.0.2",
"url-search-params-polyfill": "^8.1.0",
"winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.0",

View File

@ -1,93 +0,0 @@
/**
* React Starter Kit (https://www.reactstarterkit.com/)
*
* Copyright © 2014-present Kriasoft, LLC. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-console */
import fs from 'fs';
import path from 'path';
import webpack from 'webpack';
import webpackConfigWeb from '../webpack.config.web.babel';
import { buildWebpackClientConfig } from '../webpack.config.client.babel';
const wpStats = {
colors: true,
reasons: false,
hash: false,
version: false,
timings: true,
chunks: true,
chunkModules: false,
cached: false,
cachedAssets: false,
};
/**
* Creates application bundles from the source files.
*/
async function bundle() {
try {
/* fix image-q imports here
* Pretty dirty, but we did write an issue and they might
* update one day
*/
console.log('Patching image-q set-immediate import');
const regex = /core-js\/fn\/set-immediate/g;
const files = [
path.resolve(
__dirname, '..', 'node_modules',
'image-q', 'dist', 'esm', 'basicAPI.js',
),
path.resolve(
__dirname, '..', 'node_modules',
'image-q', 'dist', 'esm', 'helper.js',
),
];
files.forEach((file) => {
let fileContent = fs.readFileSync(file, 'utf8');
fileContent = fileContent.replace(
regex,
'core-js/features/set-immediate',
);
fs.writeFileSync(file, fileContent);
});
console.log('Patching image-q done');
} catch {
console.log('Error while patching image-q');
}
console.log('Bundle with webpack....');
let webpackConfig = [
webpackConfigWeb,
buildWebpackClientConfig(false, false, 'default'),
]
/*
* add other language configs
*/
const langDir = path.resolve(__dirname, '..', 'i18n');
const langs = fs.readdirSync(langDir)
.filter((e) => e.endsWith('.po'))
.map((l) => l.slice(0, -3));
webpackConfig = webpackConfig.concat(
langs.map((l) => buildWebpackClientConfig(false, false, l)),
);
return new Promise((resolve, reject) => {
webpack(webpackConfig).run((err, stats) => {
if (err) {
return reject(err);
}
console.log(stats.toString(wpStats));
return resolve();
});
});
}
export default bundle;

View File

@ -1,25 +1,26 @@
/*
* Minify CSS
* currently just css files for themes are loades seperately,
* so files beginning with "theme-" in the src/styles folder will
* be read and automatically added.
*
* @flow
*/
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-console */
import fs from 'fs';
import path from 'path';
import CleanCSS from 'clean-css';
import crypto from 'crypto';
const rootdir = path.resolve(__dirname, '..');
const assetdir = path.resolve(__dirname, '..', 'build', 'public', 'assets');
const builddir = path.resolve(__dirname, '..', 'build');
const FOLDER = path.resolve(__dirname, '..', 'src', 'styles');
const FILES = [
'default.css',
'dark.css',
'light-round.css',
'dark-round.css',
'arkeros.css',
];
const FILES = fs.readdirSync(FOLDER).filter((e) => e.startsWith('theme-'));
FILES.push('default.css');
async function minifyCss() {
console.log('Minifying css');
@ -37,18 +38,21 @@ async function minifyCss() {
for (let i = 0; i < output.errors.length; i += 1) {
console.log('\x1b[31m%s\x1b[0m', output.errors[i]);
}
throw new Error("Minify CSS Error Occured");
throw new Error('Minify CSS Error Occured');
}
// 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');
const key = file.substr(0, file.indexOf('.'));
let key = file.substr(0, file.indexOf('.'));
if (key.startsWith('theme-')) {
key = key.substr(6);
}
const filename = `${key}.${hash.substr(0, 8)}.css`;
fs.writeFileSync(path.resolve(assetdir,filename), output.styles, 'utf8');
fs.writeFileSync(path.resolve(assetdir, filename), output.styles, 'utf8');
assets[key] = `/assets/${filename}`;
});
const json = JSON.stringify(assets);
fs.writeFileSync(path.resolve(builddir, styleassets.json), json);
fs.writeFileSync(path.resolve(builddir, 'styleassets.json'), json);
}
export default minifyCss;

44
scripts/patch.js Normal file
View File

@ -0,0 +1,44 @@
/*
* @flow
*/
import path from 'path';
import fs from 'fs';
/* eslint-disable no-console */
function patchImageQ() {
try {
/* fix image-q imports here
* Pretty dirty, but we did write an issue and they might
* update one day
*/
console.log('Patching image-q set-immediate import');
const regex = /core-js\/fn\/set-immediate/g;
const files = [
path.resolve(
__dirname, '..', 'node_modules',
'image-q', 'dist', 'esm', 'basicAPI.js',
),
path.resolve(
__dirname, '..', 'node_modules',
'image-q', 'dist', 'esm', 'helper.js',
),
];
files.forEach((file) => {
let fileContent = fs.readFileSync(file, 'utf8');
fileContent = fileContent.replace(
regex,
'core-js/features/set-immediate',
);
fs.writeFileSync(file, fileContent);
});
console.log('Patching image-q done');
} catch {
console.log('Error while patching image-q');
}
}
export default function patch() {
patchImageQ();
}

View File

@ -12,17 +12,15 @@ import run from './run';
import clean from './clean';
import copy from './copy';
import minifyCss from './minifyCss';
import bundle from './bundle';
/**
* Compiles the project from source files into a distributable
* format and copies it to the output (build) folder.
*/
async function build() {
async function prebuild() {
await run(clean);
await run(copy);
await run(minifyCss);
await run(bundle);
}
export default build;
export default prebuild;

View File

@ -6,8 +6,8 @@
import React from 'react';
import { connect } from 'react-redux';
import { FaUser, FaPaintBrush } from 'react-icons/fa';
import { numberToString } from '../core/utils';
import { t } from 'ttag';
import { numberToString } from '../core/utils';
import type { State } from '../reducers';
@ -18,9 +18,14 @@ const OnlineBox = ({ online, totalPixels, name }) => (
{(online || name)
? (
<div className="onlinebox">
{(online) && <span title={t`User online`}>{online} <FaUser />&nbsp;</span>}
{(online)
&& <span title={t`User online`}>{online} <FaUser />&nbsp;</span>}
{(name != null)
&& <span title={t`Pixel gesetzt`}>{numberToString(totalPixels)} <FaPaintBrush /></span>}
&& (
<span title={t`Pixel gesetzt`}>
{numberToString(totalPixels)} <FaPaintBrush />
</span>
)}
</div>
) : null}
</div>

View File

@ -28,9 +28,14 @@ function onKeyPress(event: KeyboardEvent) {
* we check if the key location is where a
* key that is used would be on QWERTY
*/
const key = (usedKeys.includes(event.key))
? event.key
: event.code.substr(-1).toLowerCase();
let { key } = event;
if (!usedKeys.includes(key)) {
key = event.code;
if (!key.startsWith('Key')) {
return;
}
key = key.substr(-1).toLowerCase();
}
switch (key) {
case 'g':

34
src/core/ttag.js Normal file
View File

@ -0,0 +1,34 @@
/*
* Provide translation serverside
* @flow
*/
import { TTag } from 'ttag';
import deLocale from '../../i18n/ssr-de.po';
const LOCALES = {
de: deLocale,
};
const ttags = {
default: new TTag(),
};
function populateTTags() {
const langs = Object.keys(LOCALES);
langs.forEach((lang) => {
const ttag = new TTag();
ttag.addLocale(lang, LOCALES[lang]);
ttag.useLocale(lang);
ttags[lang] = ttag;
});
}
populateTTags();
export function getTTag(lang) {
if (ttags[lang]) {
return ttags[lang];
}
return ttags.default;
}
export default ttags.default;

View File

@ -7,6 +7,8 @@
import React from 'react';
import ReactDOM from 'react-dom/server';
import { getTTag } from '../core/ttag';
import Html from './Html';
/* this will be set by webpack */
// eslint-disable-next-line import/no-unresolved
@ -15,14 +17,6 @@ import { ASSET_SERVER } from '../core/config';
import globeCss from '../styles/globe.css';
const Globe = () => (
<div>
<div id="webgl" />
<div id="coorbox">(0, 0)</div>
<div id="info">Double click on globe to go back.</div>
<div id="loading">Loading...</div>
</div>
);
const styles = [{
id: 'globe',
cssText: globeCss,
@ -33,7 +27,6 @@ const description = 'pixelplanet globe';
const defaultScripts = assets.globe.js.map(
(s) => ASSET_SERVER + s,
);
const body = <Globe />;
/*
* generates string with html of globe page
@ -45,12 +38,22 @@ function generateGlobePage(lang: string): string {
? assets[`globe-${lang}`].js.map((s) => ASSET_SERVER + s)
: defaultScripts;
const { t } = getTTag(lang);
const Globe = () => (
<div>
<div id="webgl" />
<div id="coorbox">(0, 0)</div>
<div id="info">{t`Double click on globe to go back.`}</div>
<div id="loading">{t`Loading...`}</div>
</div>
);
const html = ReactDOM.renderToStaticMarkup(
<Html
title={title}
description={description}
scripts={scripts}
body={body}
body={<Globe />}
styles={styles}
/>,
);

View File

@ -2,6 +2,7 @@
*/
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import AssetsPlugin from 'assets-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
@ -18,8 +19,12 @@ const assetPlugin = new AssetsPlugin({
prettyPrint: true,
});
export function buildWebpackClientConfig(development, analyze, locale) {
const ttag = {
extract: {
output: path.resolve(__dirname, 'i18n', 'template.pot'),
},
resolve: {
translations: (locale !== 'default')
? path.resolve(__dirname, 'i18n', `${locale}.po`)
@ -180,13 +185,41 @@ export function buildWebpackClientConfig(development, analyze, locale) {
},
},
stats: {
colors: true,
reasons: false,
hash: false,
version: false,
timings: true,
chunkModules: false,
},
cache: true,
};
}
export default buildWebpackClientConfig(
/*
* return array of webpack configuartions for all languages
*/
function buildWebpackClientConfigAllLangs(development, analyze) {
let webpackConfigClient = [
buildWebpackClientConfig(development, analyze, 'default'),
];
/*
* get available translations
*/
const langDir = path.resolve(__dirname, 'i18n');
const langs = fs.readdirSync(langDir)
.filter((e) => (e.endsWith('.po') && !e.startsWith('ssr')))
.map((l) => l.slice(0, -3));
webpackConfigClient = webpackConfigClient.concat(
langs.map((l) => buildWebpackClientConfig(development, analyze, l)),
);
return webpackConfigClient;
}
export default buildWebpackClientConfigAllLangs(
process.argv.includes('--debug'),
process.argv.includes('--analyse') || process.argv.includes('--analyze'),
'default',
);

View File

@ -1,4 +1,10 @@
/**
* This is an old webpack config that uses ttag-webpack-plugin
* Sadly this plugin has issues with webpack 5, so we can't use it.
* But we keep this config around, so that we can adjust in the case it
* might become stable.
*
* https://github.com/ttag-org/ttag-webpack-plugin/issues
*/
import fs from 'fs';

View File

@ -2,14 +2,28 @@
*/
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import nodeExternals from 'webpack-node-externals';
import GeneratePackageJsonPlugin from 'generate-package-json-webpack-plugin';
import patch from './scripts/patch';
import pkg from './package.json';
const isDebug = process.argv.includes('--debug');
/*
* check which ssr translations are available
* Maybe we will use thi later to auto-populat src/core/ttag.js
*
const langDir = path.resolve(__dirname, 'i18n');
const langs = fs.readdirSync(langDir)
.filter((e) => (e.endsWith('.po') && e.startsWith('ssr')));
fs.writeFileSync(path.resolve(langDir, 'ssr-list.json'), JSON.stringify(langs));
*/
patch();
const basePackageValues = {
name: pkg.name,
version: pkg.version,
@ -37,6 +51,12 @@ const babelPlugins = [
'@babel/transform-react-inline-elements',
'transform-react-remove-prop-types',
'transform-react-pure-class-to-function',
['ttag', {
extract: {
output: path.resolve(__dirname, 'i18n', 'template-ssr.pot'),
},
discover: ['t', 'jt', 'gettext'],
}],
];
@ -101,6 +121,10 @@ export default {
},
],
},
{
test: [/\.po$/],
loader: 'ttag-po-loader',
},
],
},
@ -125,6 +149,15 @@ export default {
}),
],
stats: {
colors: true,
reasons: false,
hash: false,
version: false,
timings: true,
chunkModules: false,
},
node: {
global: false,
__filename: false,