Brief overview
I am struggling with porting a react app to be es5 compatible. We have special requirements to run the app on Chrome v37. Therefore i added es5 compatible polyfills, shimming and transpiling to the toolchain
Tools and basic configuration
- Webpack (v5+), babel (v7+), npm, and typescript (v4.7+)
- core-js (v3.23+) for polyfills
- bundle file for our sources
- bundle file for vendor sources
- node_modules which are not es5 compatible are NOT excluded from babel-loader
- node_modules which are es5 compatible are excluded from babel-loader
I also tried:
- use different module types in the typescript.config (CommonJS, AMD and ES6)
- use different properties for strict mode in tthe typescript.config
- downgrade rtf.js package (but don't go under 3.0 because the author removed jquery with 3.0)
Problem
Modern browsers do not throw any error. Old browser does.
The error states: 'Invalid left-hand side in assignment' in the vendor.bundle.js file on 'push.../node_modules/rtf.js/dist/RTFJS.bundle.js'
Chrome v37 throws following error:
Uncaught ReferenceError: Invalid left-hand side in assignment
vendors.c543a78e.js:10131push.../node_modules/rtf.js/dist/RTFJS.bundle.js
vendors.c543a78e.js:10131__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function) index.js:7push.../node_modules/rtf.js/index.js
vendors.c543a78e.js:10152__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function)
RtfTextConverter.ts?111d:4__webpack_modules__../controllers/sceneInterpreters
/compositeSceneInterpreter/RtfTextConverter.ts app.c543a78e.js:1533__webpack_require__
app.c543a78e.js:1802fn app.c543a78e.js:2048(anonymous function)
CompositeSceneInterpreter.ts?8981:16__webpack_modules__../controllers/sceneInterpreters
/compositeSceneInterpreter/CompositeSceneInterpreter.ts
app.c543a78e.js:1523__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function)
PlaylistInterpreter.ts?e5ab:6__webpack_modules__../PlaylistInterpreter.ts
app.c543a78e.js:543__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function)
PlaylistManager.tsx?4d1a:5__webpack_modules__../PlaylistManager.tsx
app.c543a78e.js:553__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function)
KeyConfigurer.ts?e3b4:8__webpack_modules__../KeyConfigurer.ts
app.c543a78e.js:493__webpack_require__ app.c543a78e.js:1802fn
app.c543a78e.js:2048(anonymous function) Main.tsx?e426:5__webpack_modules__../Main.tsx
app.c543a78e.js:503__webpack_require__ app.c543a78e.js:1802(anonymous function)
app.c543a78e.js:2958__webpack_require__.O app.c543a78e.js:1851(anonymous function)
app.c543a78e.js:2959(anonymous function)
Build Configuration
webpack.config.json
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SignageOSPlugin = require('@signageos/webpack-plugin');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const Dotenv = require('dotenv-webpack');
const fs = require('fs');
var GitRevisionPlugin = require('git-revision-webpack-plugin');
var gitRevisionPlugin = new GitRevisionPlugin({
commithashCommand: 'rev-list --max-count=1 --no-merges HEAD',
versionCommand: 'rev-list HEAD --count',
branchCommand: 'status --porcelain',
});
module.exports = {
optimization: {
minimize: false,
// put all third-party libs into separate bundle file.
// (https://webpack.js.org/plugins/split-chunks-plugin/)
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
new webpack.DefinePlugin({
__COMMITHASH__: JSON.stringify(gitRevisionPlugin.commithash()),
__COMMITS_TOWARDS_HEAD__: JSON.stringify(gitRevisionPlugin.version()),
__GIT_STATUS__: JSON.stringify(gitRevisionPlugin.branch()),
}),
new HtmlWebpackPlugin({
template: '../index_web.html',
}),
new Dotenv(),
new webpack.HotModuleReplacementPlugin(),
new SignageOSPlugin(),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
],
entry: {
app: ['core-js', 'whatwg-fetch', 'raf/polyfill', './Main.tsx'],
},
mode: 'development',
devServer: {
host: '0.0.0.0',
},
devtool: 'eval-cheap-module-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: new RegExp(fs.readFileSync(path.resolve('./non_ES5_node_modules'), 'utf-8').slice(1, -2)), // /[\\/]node_modules[\\/](?!(free-style|rtf.js|rtfjs)[\\/])/,
},
{
/*
* https://github.com/webpack-contrib/html-loader/pull/116/commits/843c76eef19122142505ef3c4611ec5b2c70ba24#diff-04c6e90faac2675aa89e2176d2eec7d8
* https://github.com/dbailo1988/html-loader#examples
*/
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: {
minimize: false,
},
},
],
},
{
test: /^(.(?!.module.css))*.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: ['/.jsx?$/', '/.js$/', '/.m?js$/'],
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
[
// disabling strict mode for babel transform because free-style lib would fail
'@babel/plugin-transform-modules-commonjs',
{
loose: true,
strictMode: false,
},
],
],
},
},
exclude: new RegExp(fs.readFileSync(path.resolve('./non_ES5_node_modules'), 'utf-8').slice(1, -2)), // /[\\/]node_modules[\\/](?!(free-style|rtf.js|rtfjs)[\\/])/,
},
],
},
context: path.resolve(__dirname, 'src'),
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: ['src', 'node_modules'],
fallback: { stream: require.resolve('stream-browserify') },
alias: {
process: 'process/browser', // shimming process/browser for chrome v37
'free-style': 'free-style/dist.es5/index.js', // avoid es2015 build and use es5 dist files for free-style
},
},
output: {
path: path.resolve(__dirname, './dist/'),
filename: '[name].[hash:8].js',
sourceMapFilename: '[name].[hash:8].map',
chunkFilename: '[id].[hash:8].js',
},
};
tsconfig.json
{
"extends": "./node_modules/gts/tsconfig-google.json",
"compilerOptions": {
"outDir": "./dist/",
"module": "ES6", //es6
"target": "ES5",
"allowJs": true,
"sourceMap": true,
"strict": false,
"jsx": "react",
"noEmit": false,
"moduleResolution": "node",
"esModuleInterop": true,
// es2015.promise -> required for promises
// es2017 -> required for maps/sets
"lib": ["es2017", "dom"],
// related to jest
//"allowSyntheticDefaultImports": true
"typeRoots": ["../node_modules/@types"],
"skipLibCheck": true,
// "alwaysStrict": true,
// disabling strict because free-style lib would fail
"noImplicitUseStrict": true
},
"exclude": ["node_modules", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts", "test/**/*.ts", "typings/**/*.d.ts", "src/**/*"]
}
package.json
{
"name": "newsrodeoviewer",
"version": "2.0.3",
"description": "NewsRodeo Viewer built with SignageOS middleware",
"private": true,
"main": "dist/index.html",
"browserslist": [
"since 2014",
"dead",
"chrome 37",
"chrome 38"
],
"scripts": {
"start": "webpack-dev-server --mode development",
"prepare": "npm run clean && npm run build",
"upload": "sos applet upload",
"clean": "gts clean",
"escheck": "npx es-check --module=false es5 dist/*.js",
"build": "webpack --mode production",
"postbuild": "npm run escheck",
"connect": "webpack --watch",
"lint": "gts lint",
"test": "jest --watch",
"test-failures": "jest --onlyFailures",
"test-unit": "jest unit.test",
"test-int": "jest int.tes",
"dev-build": "npx webpack --mode development",
"prod-build": "npx webpack --mode production",
"compile": "tsc",
"fix": "gts fix",
"postinstall": "sh -c \"npx --silent are-you-es5 check -r . | tail -n 1 > ./non_ES5_node_modules \""
},
"keywords": [],
"author": "",
"files": [
"dist"
],
"devDependencies": {
"@babel/core": "^7.17.10",
"@babel/preset-env": "^7.17.10",
"@signageos/cli": "^1.0.2",
"@signageos/webpack-plugin": "^0.3.0",
"@types/jest": "^28.1.3",
"@types/node": "^14.18.21",
"@types/node-fetch": "^2.1.2",
"@types/react": "^18.0.12",
"@types/uuid": "^8.3.4",
"amd-define-factory-patcher-loader": "^1.0.0",
"are-you-es5": "^2.1.2",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"dotenv-webpack": "^7.1.1",
"git-revision-webpack-plugin": "^3.0.3",
"gts": "^3.1.0",
"html-loader": "^3.1.2",
"html-webpack-plugin": "^5.5.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^28.1.1",
"jest-css-modules": "^1.1.0",
"jest-environment-jsdom": "^28.1.1",
"node-fetch": "^2.2.0",
"source-map-loader": "^4.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^28.0.5",
"ts-loader": "^9.3.1",
"tslint": "^5.11.0",
"tslint-react": "^3.6.0",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3",
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
"@signageos/front-applet": "^5.3.0",
"@signageos/front-display": "^9.16.2",
"@types/crypto-js": "^3.1.43",
"@types/jquery": "^3.5.14",
"@types/react-dom": "^18.0.4",
"@types/react-redux": "^7.1.24",
"async-mutex": "^0.1.3",
"circularbuffer": "^0.1.1",
"core-js": "^3.23.3",
"crypto-js": "^3.1.9-1",
"free-style": "^3.2.4",
"http-status-codes": "^1.3.0",
"moment": "^2.29.3",
"raf": "^3.4.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons-kit": "^2.0.0",
"react-live-clock": "^5.10.0",
"react-redux": "^8.0.2",
"react-spinkit": "^3.0.0",
"redux": "^4.2.0",
"regenerator-runtime": "^0.13.9",
"rtf.js": "^3.0.1",
"stream-browserify": "^3.0.0",
"typesafe-actions": "^2.0.4",
"typestyle": "^2.3.0",
"uuid": "^8.3.2",
"whatwg-fetch": "^3.6.2",
"xml-js": "^1.6.7"
}
}
non_ES5_node_modules
/[\\/]node_modules[\\/](?!(free-style|rtf.js)[\\/])/
I really need to solve this and have no clue what to do. Please feel free to ask for additional information if necessary. Thank you very much!