diff --git a/babel.config.js b/babel.config.js
index 9598a2a..e85b2b1 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -9,7 +9,8 @@ module.exports = api => {
'@babel/plugin-transform-runtime',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-numeric-separator',
- '@babel/plugin-proposal-throw-expressions'
+ '@babel/plugin-proposal-throw-expressions',
+ '@loadable/babel-plugin'
];
const basePresets = [];
diff --git a/lib/cli.js b/lib/cli.js
index 6cea134..9ec323d 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -1,7 +1,7 @@
import arg from 'arg';
import fs from 'fs';
import path from 'path';
-import clc from "cli-color";
+import clc from 'cli-color';
import { spawn } from 'child_process';
import normalizeUrl from './os';
@@ -19,10 +19,10 @@ function parseArgumentsIntoOptions(rawArgs) {
'--no-bundle': Boolean,
'--analyze': Boolean,
'--port': Number,
- '--ssr': Boolean,
+ '--ssr': Boolean
},
{
- argv: rawArgs.slice(2),
+ argv: rawArgs.slice(2)
}
);
const argsList = removeUnneccesaryValueInObject({
@@ -32,17 +32,18 @@ function parseArgumentsIntoOptions(rawArgs) {
noBundle: args['--no-bundle'],
analyze: args['--analyze'],
configFile: args['--config'],
- ssr: args['--ssr'],
+ ssr: args['--ssr']
});
return argsList;
}
function getVoltranConfigs(configFile) {
- const normalizePath = normalizeUrl(path.resolve(__dirname));
- const dirName = normalizePath.indexOf('node_modules') > -1 ?
- normalizePath.split('/node_modules')[0] :
- normalizePath.split('voltran/lib')[0] + 'voltran';
+ const normalizePath = normalizeUrl(path.resolve(__dirname));
+ const dirName =
+ normalizePath.indexOf('node_modules') > -1
+ ? normalizePath.split('/node_modules')[0]
+ : `${normalizePath.split('voltran/lib')[0]}voltran`;
const voltranConfigs = require(path.resolve(dirName, configFile));
return voltranConfigs;
@@ -68,35 +69,40 @@ function runDevelopmentMode() {
function runProductionMode(voltranConfigs, onlyBundle) {
const bundle = require('../src/tools/bundle');
- bundle()
- .then((res) => {
- console.log(clc.green('Bundle is completed.\n',`File: ${voltranConfigs.distFolder}/server/server.js`));
+ bundle().then(() => {
+ console.log(
+ clc.green('Bundle is completed.\n', `File: ${voltranConfigs.distFolder}/server/server.js`)
+ );
- if (!onlyBundle) {
- serve(voltranConfigs);
- }
- });
+ if (!onlyBundle) {
+ serve(voltranConfigs);
+ }
+ });
}
function serve(voltranConfigs) {
console.log(clc.green('Project Serve is starting...'));
- const out = spawn('node', [
- '-r',
- 'source-map-support/register',
- '--max-http-header-size=20480',
- `${voltranConfigs.distFolder}/server/server.js`
- ], {env: {'NODE_ENV': 'production', ...process.env}});
+ const out = spawn(
+ 'node',
+ [
+ '-r',
+ 'source-map-support/register',
+ '--max-http-header-size=20480',
+ `${voltranConfigs.distFolder}/server/server.js`
+ ],
+ { env: { NODE_ENV: 'production', ...process.env } }
+ );
- out.stdout.on('data', (data) => {
+ out.stdout.on('data', data => {
console.log(data.toString());
});
- out.stderr.on('data', (data) => {
+ out.stderr.on('data', data => {
console.error(data.toString());
});
- out.on('close', (code) => {
+ out.on('close', code => {
console.log(`child process exited with code ${code}`);
});
}
@@ -123,7 +129,7 @@ export function cli(args) {
if (isValid) {
const createdConfig = `module.exports = ${JSON.stringify(mergeAllConfigs)}`;
- fs.writeFile(path.resolve(__dirname, '../voltran.config.js'), createdConfig, function (err) {
+ fs.writeFile(path.resolve(__dirname, '../voltran.config.js'), createdConfig, function(err) {
if (err) throw err;
console.log('File is created successfully.', mergeAllConfigs.dev);
@@ -131,9 +137,9 @@ export function cli(args) {
if (mergeAllConfigs.dev) {
runDevelopmentMode();
} else {
- argumentList.noBundle ?
- serve(voltranConfigs) :
- runProductionMode(mergeAllConfigs, argumentList.bundle);
+ argumentList.noBundle
+ ? serve(voltranConfigs)
+ : runProductionMode(mergeAllConfigs, argumentList.bundle);
}
});
} else {
diff --git a/package.json b/package.json
index ba2d6ed..5e4c938 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,10 @@
"@babel/plugin-transform-runtime": "7.10.4",
"@babel/preset-env": "7.14.4",
"@babel/preset-react": "7.0.0",
- "@researchgate/react-intersection-observer": "1.0.3",
+ "@loadable/babel-plugin": "^5.12.0",
+ "@loadable/component": "^5.12.0",
+ "@loadable/server": "^5.12.0",
+ "@loadable/webpack-plugin": "^5.12.0",
"arg": "^4.1.3",
"assets-webpack-plugin": "3.8.4",
"async": "^3.2.0",
@@ -37,7 +40,6 @@
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-loader": "^8.0.4",
- "classnames": "2.2.6",
"clean-webpack-plugin": "1.0.0",
"cli-color": "^2.0.0",
"compose-middleware": "5.0.0",
@@ -51,18 +53,17 @@
"eslint": "6.1.0",
"eslint-config-airbnb": "18.0.1",
"eslint-config-prettier": "6.3.0",
+ "eslint-plugin-babel": "^5.3.1",
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-prettier": "3.1.1",
"eslint-plugin-react": "7.14.3",
+ "eslint-plugin-react-hooks": "^4.2.0",
"esm": "^3.2.25",
"file-loader": "1.1.11",
"helmet": "3.21.3",
"hiddie": "^1.0.0",
"husky": "^3.1.0",
- "identity-obj-proxy": "3.0.0",
- "intersection-observer": "0.7.0",
- "js-cookie": "^2.2.1",
"lodash": "4.17.19",
"mini-css-extract-plugin": "0.4.4",
"newrelic": "^6.13.0",
@@ -78,7 +79,6 @@
"prop-types": "15.6.2",
"query-string": "6.10.1",
"react": "16.12.0",
- "react-autosuggest": "9.4.3",
"react-dom": "16.12.0",
"react-hot-loader": "^4.12.18",
"react-router": "5.1.2",
diff --git a/src/main.js b/src/main.js
index fa6bdf2..ed735a0 100644
--- a/src/main.js
+++ b/src/main.js
@@ -4,14 +4,15 @@ import cluster from 'cluster';
import logger from './universal/utils/logger';
import Hiddie from 'hiddie';
import http from 'http';
-import voltranConfig from '../voltran.config';
import prom from 'prom-client';
-import {HTTP_STATUS_CODES} from './universal/utils/constants';
+
+import voltranConfig from '../voltran.config';
+import { HTTP_STATUS_CODES } from './universal/utils/constants';
const enablePrometheus = voltranConfig.monitoring.prometheus;
function triggerMessageListener(worker) {
- worker.on('message', function (message) {
+ worker.on('message', function(message) {
if (message?.options?.forwardAllWorkers) {
sendMessageToAllWorkers(message);
}
@@ -19,15 +20,15 @@ function triggerMessageListener(worker) {
}
function sendMessageToAllWorkers(message) {
- Object.keys(cluster.workers).forEach(function (key) {
+ Object.keys(cluster.workers).forEach(function(key) {
const worker = cluster.workers[key];
worker.send({
- msg: message.msg,
+ msg: message.msg
});
}, this);
}
-cluster.on('fork', (worker) => {
+cluster.on('fork', worker => {
triggerMessageListener(worker);
});
@@ -52,7 +53,7 @@ if (cluster.isMaster) {
return res.end(await aggregatorRegistry.clusterMetrics());
}
res.statusCode = HTTP_STATUS_CODES.NOT_FOUND;
- res.end(JSON.stringify({message: 'not found'}));
+ res.end(JSON.stringify({ message: 'not found' }));
});
http.createServer(hiddie.run).listen(metricsPort, () => {
diff --git a/src/universal/components/Html.js b/src/universal/components/Html.js
index 61cf88e..6b51678 100644
--- a/src/universal/components/Html.js
+++ b/src/universal/components/Html.js
@@ -24,7 +24,7 @@ function componentClassName(componentName, context) {
function Html({
componentName,
- children,
+ bodyHtml,
styleTags,
initialState,
fullWidth,
@@ -41,7 +41,7 @@ function Html({
class="${voltranConfig.prefix}-voltran-body voltran-body ${
isMobileFragment ? 'mobile' : ''
}${fullWidth ? 'full' : ''} ${componentClassName(componentName, context)}">
- ${children}
+ ${bodyHtml}
REPLACE_WITH_LINKS
REPLACE_WITH_SCRIPTS
diff --git a/src/universal/partials/withBaseComponent.js b/src/universal/partials/withBaseComponent.js
index 0f78d45..d69aab6 100644
--- a/src/universal/partials/withBaseComponent.js
+++ b/src/universal/partials/withBaseComponent.js
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import { loadableReady } from '@loadable/component';
import ClientApp from '../components/ClientApp';
import { WINDOW_GLOBAL_PARAMS } from '../utils/constants';
@@ -35,16 +36,18 @@ const withBaseComponent = (PageComponent, pathName) => {
const initialState = fragments[id].STATE;
- ReactDOM.hydrate(
-
-
- ,
- componentEl,
- () => {
- componentEl.style.pointerEvents = 'auto';
- componentEl.setAttribute('voltran-hydrated', 'true');
- }
- );
+ loadableReady(() => {
+ ReactDOM.hydrate(
+
+
+ ,
+ componentEl,
+ () => {
+ componentEl.style.pointerEvents = 'auto';
+ componentEl.setAttribute('voltran-hydrated', 'true');
+ }
+ );
+ });
});
}
diff --git a/src/universal/service/RenderService.js b/src/universal/service/RenderService.js
index 33acc48..4d6ed90 100644
--- a/src/universal/service/RenderService.js
+++ b/src/universal/service/RenderService.js
@@ -55,7 +55,7 @@ const renderHtml = (component, initialState, context) => {
return PureHtml(component.path, component.name, initialStateWithLocation);
}
- const children = ReactDOMServer.renderToString(
+ const bodyHtml = ReactDOMServer.renderToString(
sheet.collectStyles(
@@ -69,7 +69,7 @@ const renderHtml = (component, initialState, context) => {
return Html({
resultPath,
componentName: component.name,
- children,
+ bodyHtml,
styleTags,
initialState: initialStateWithLocation,
fullWidth: component.fullWidth,
diff --git a/src/universal/utils/baseRenderHtml.js b/src/universal/utils/baseRenderHtml.js
index a0ef08f..5cac9ce 100644
--- a/src/universal/utils/baseRenderHtml.js
+++ b/src/universal/utils/baseRenderHtml.js
@@ -14,16 +14,21 @@ const cleanAssetsPrefixFromAssetURI = assetURI => {
const cssContentCache = {};
-if (process.env.NODE_ENV === 'production') {
- Object.keys(assets).forEach(name => {
- if (assets[name].css) {
- cssContentCache[name] = fs.readFileSync(
- path.resolve(
- process.cwd(),
- `${voltranConfig.publicDistFolder}/${cleanAssetsPrefixFromAssetURI(assets[name].css)}`
- ),
- 'utf8'
- );
+if (process.env.NODE_ENV === 'production' && assets.assetsByChunkName) {
+ Object.keys(assets.assetsByChunkName).
+ forEach(name => {
+ if (assets.assetsByChunkName[name] && Array.isArray(assets.assetsByChunkName[name]) && assets.assetsByChunkName[name].length > 0) {
+ assets.assetsByChunkName[name].map(fileName => {
+ if (fileName.endsWith('.css')) {
+ cssContentCache[name] = fs.readFileSync(
+ path.resolve(
+ process.cwd(),
+ `${voltranConfig.publicDistFolder}/${cleanAssetsPrefixFromAssetURI(fileName)}`,
+ ),
+ 'utf8',
+ );
+ }
+ });
}
});
}
@@ -33,12 +38,12 @@ const getScripts = (name, subComponentFiles) => {
const scripts = [
{
src: `${assetsBaseUrl}${assets.client.js}`,
- isAsync: false
+ isAsync: false,
},
{
src: `${assetsBaseUrl}${assets[name].js}`,
- isAsync: false
- }
+ isAsync: false,
+ },
];
const mergedScripts =
subComponentFilesScripts && subComponentFilesScripts.length > 0
@@ -48,7 +53,7 @@ const getScripts = (name, subComponentFiles) => {
return mergedScripts;
};
-const getStyles = async (name, subComponentFiles) => {
+const getStyles = async(name, subComponentFiles) => {
const links = [];
const subComponentFilesStyles = subComponentFiles.styles;
@@ -59,7 +64,7 @@ const getStyles = async (name, subComponentFiles) => {
criticalStyleComponent:
process.env.NODE_ENV === 'production' && !voltranConfig.criticalCssDisabled
? cssContentCache[name]
- : undefined
+ : undefined,
});
}
@@ -70,7 +75,7 @@ const getStyles = async (name, subComponentFiles) => {
criticalStyleComponent:
process.env.NODE_ENV === 'production' && !voltranConfig.criticalCssDisabled
? cssContentCache.client
- : undefined
+ : undefined,
});
}
@@ -88,15 +93,15 @@ const getActiveComponent = name => {
return {
resultPath: path,
componentName: name,
- url: path
+ url: path,
};
};
-const createBaseRenderHtmlProps = async (name, subComponentFiles) => {
+const createBaseRenderHtmlProps = async(name, subComponentFiles) => {
return {
scripts: getScripts(name, subComponentFiles),
links: await getStyles(name, subComponentFiles),
- activeComponent: getActiveComponent(name)
+ activeComponent: getActiveComponent(name),
};
};
diff --git a/webpack.client.config.js b/webpack.client.config.js
index cb0adfe..dbbaae3 100644
--- a/webpack.client.config.js
+++ b/webpack.client.config.js
@@ -10,9 +10,7 @@ const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
-const { ESBuildMinifyPlugin } = require('esbuild-loader');
-
-require('intersection-observer');
+const LoadablePlugin = require('@loadable/webpack-plugin');
const { createComponentName } = require('./src/universal/utils/helper.js');
@@ -34,7 +32,6 @@ const voltranClientConfig = voltranClientConfigPath
? require(voltranConfig.webpackConfiguration.client)
: '';
-const normalizeUrl = require('./lib/os.js');
const replaceString = require('./config/string.js');
const fragmentManifest = require(voltranConfig.routing.dictionary);
@@ -42,7 +39,6 @@ const fragmentManifest = require(voltranConfig.routing.dictionary);
const isDebug = voltranConfig.dev;
const reScript = /\.(js|jsx|mjs)$/;
const distFolderPath = voltranConfig.distFolder;
-const prometheusFile = voltranConfig.monitoring.prometheus;
const chunks = {};
@@ -117,11 +113,13 @@ const clientConfig = webpackMerge(commonConfig, voltranClientConfig, {
rules: [
{
test: reScript,
- loader: 'esbuild-loader',
+ loader: 'babel-loader',
include: [path.resolve(__dirname, 'src'), voltranConfig.inputFolder],
options: {
- loader: 'jsx',
- target: 'es2015'
+ cacheDirectory: isDebug,
+ babelrc: false,
+ plugins: ['@loadable/babel-plugin'],
+ ...babelConfig()
}
},
{
@@ -209,10 +207,6 @@ const clientConfig = webpackMerge(commonConfig, voltranClientConfig, {
optimization: {
minimizer: [
- new ESBuildMinifyPlugin({
- target: 'es2015',
- css: true
- }),
new TerserWebpackPlugin({
sourceMap: isDebug,
parallel: true,
@@ -237,6 +231,15 @@ const clientConfig = webpackMerge(commonConfig, voltranClientConfig, {
GO_PIPELINE_LABEL: JSON.stringify(GO_PIPELINE_LABEL)
}),
+ new LoadablePlugin({
+ filename: path.resolve(
+ process.cwd(),
+ `${voltranConfig.inputFolder}/universal/loadable-stats.json`
+ ),
+ writeToDisk: true,
+ outputAsset: false
+ }),
+
new CopyWebpackPlugin([
{
from: voltranConfig.output.client.publicPath,
diff --git a/webpack.server.config.js b/webpack.server.config.js
index 1b9b79b..ecb0cf7 100644
--- a/webpack.server.config.js
+++ b/webpack.server.config.js
@@ -10,6 +10,7 @@ const voltranConfig = require('./voltran.config');
const appConfigFilePath = `${voltranConfig.appConfigFile.entry}/${env}.conf.js`;
const appConfig = require(appConfigFilePath); // eslint-disable-line import/no-dynamic-require
+const babelConfig = require('./babel.server.config');
const commonConfig = require('./webpack.common.config');
const postCssConfig = require('./postcss.config');
const replaceString = require('./config/string.js');
@@ -47,11 +48,12 @@ const serverConfig = webpackMerge(commonConfig, voltranServerConfig, {
rules: [
{
test: /\.(js|jsx|mjs)$/,
- loader: 'esbuild-loader',
+ loader: 'babel-loader',
include: [path.resolve(__dirname, 'src'), voltranConfig.inputFolder],
options: {
- loader: 'jsx',
- target: 'es2015'
+ cacheDirectory: true,
+ babelrc: false,
+ ...babelConfig()
}
},
{