Webpack prerender-spa-plugin with compression-webpack-plugin. index.html not compressed - javascript

I'm building a vue cli 3 app with vue-cli-plugin-compression and vue-cli-plugin-prerender-spa installed. (Under the hood these use prerender-spa-plugin and compression-webpack-plugin).
The prerender-spa-plugin renames index.html to app.html. It then prerenders app.html and stores the resulting html in a new index.html. The page is prerendered correctly and app.html is correctly gzipped. However, the resulting index.html (the page that is the result of the prerendering) is not gzipped. How can I get the result of the prerender to be gzipped as well?
Here's my vue.config.js:
module.exports = {
devServer: {
port: 3000
},
configureWebpack: {
devtool: 'source-map'
},
pluginOptions: {
prerenderSpa: {
customRendererConfig: {
injectProperty: '__PRERENDER_INJECTED',
inject: {},
},
registry: undefined,
renderRoutes: [
'/'
],
useRenderEvent: true,
headless: true,
onlyProduction: true,
postProcess: route => {
// Defer scripts and tell Vue it's been server rendered to trigger hydration
route.html = route.html
.replace(/<script (.*?)>/g, '<script $1 defer>')
.replace('id="app"', 'id="app" data-server-rendered="true"');
return route;
}
},
compression:{
gzip: {
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|js\.map|css|html)$/,
minRatio: 0.8,
}
}
}
};
I tried to prerender before compression, but it doesn't change anything:
chainWebpack: (config) => {
config.plugin('pre-render').before('gzip-compression');
config.plugin('gzip-compression').after('html');
},

So, it turns out that the prerender-spa-plugin is outdated and only works for webpack 4, most of the issues have been overcome in webpack 5 with new hooks
So I refactored the code base of prerender-spa-plugin to work for webpack 5 (and only for it), I also had to remove some features like the html minification as now other compression plugins will correctly run on the html
You can find the package on npm prerender-spa-plugin-next
You will need to update your vue cli plugins to the version ^5 to use webpack 5
As of writing:
"#vue/cli-plugin-babel": "^5.0.4",
"#vue/cli-plugin-eslint": "^5.0.4",
"#vue/cli-plugin-router": "^5.0.4",
"#vue/cli-service": "^5.0.4",
"compression-webpack-plugin": "^6.1.1",
"html-webpack-plugin": "^5.3.1",
...
Make sure all of your other dependencies are also updated (Eslint and all the webpack plugins and loaders)
This might turn into a lot of trial and error to get it to compile after a big update but the trouble is worth it
Let me know if you have any question regarding the usage of my package

Related

Disable the bundle.js bundling file Webpack creates in React using Create React App or Craco? Or at least get 'Performance' to work with source maps

How can I turn off bundling completely in create react app or craco? I'm trying to use craco to do it and my config for webpack is as follows:
configure: {
/* Any webpack configuration options: https://webpack.js.org/configuration */
mode: 'development',
optimization: {
minimize: false,
},
devtool: 'eval-source-map',
},
I need to turn off bundling because even with source maps, the performance analyzer (Waterfall) in Firefox still shows some calls coming from bundle.js, which is... Not helpful at all since some calls are coming from "bundle.js line 3051 %3E eval:81828" - and when clicked, say "line not found". The Call Tree in the Performance tab also just shows nothing but bundle.js (eval:####) calls. When THOSE lines are clicked, it brings me to code that looks like:
I'm trying to optimize a Phaser webgl game and bundling is making things very difficult. Any help would be appreciated.
Oh and another weird thing - when looking at the Waterfall record for a Phaser call - it usually starts off with the weird broken "bundle.js%20line%20####%20%3E%20eval:#######" call, but has phaser.js calls below that with working links to the line of code that's calling it.
Relevant package.json dependencies:
"#craco/craco": "^7.0.0-alpha.3",
"phaser": "^3.55.2",
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.0",
You can achieve this with cracko config.
module.exports = {
webpack: {
configure: {
output: {
filename: 'static/js/[name].js'
},
optimization: {
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false;
}
}
}
}
},
plugins: [
{
plugin: {
overrideWebpackConfig: ({ webpackConfig }) => {
webpackConfig.plugins[5].options.filename = 'static/css/[name].css';
return webpackConfig;
}
},
options: {}
}
]
};

How to import a library bundled with webpack 5 in an html script tag

While trying to migrate from webpack 4 to webpack 5, my library fails to load within an html script tag. I have found many posts discussing how to do it with webpack 4 but none for webpack 5.
My current minimal project is made of :
// src/index.js
function test() {
return 42
}
export {test}
// webpack.config.js
module.exports = {
mode: "development",
devServer: {
port: 3030,
},
entry: {
lib: "./src/index.js",
},
output: {
filename: `[name].js`,
libraryTarget: "umd",
library: "MyLib"
},
}
// index.html
<script src="http://localhost:3030/lib.js"></script>
<script>
try { console.log(MyLib); } catch (error) { console.warn("error MyLib")}
try { console.log(window.MyLib); } catch (error) { console.warn("error window.MyLib")}
try { console.log(window.default); } catch (error) { console.warn("error window.default")}
</script>
// package.json dependencies
"webpack": "^5.50.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
I am expected to see a function signature at some point but everything is undefined.
I am probably missing something obvious but... I haven't found the answer yet!
The problem was only for npx webpack serve and not npx webpack. When building the library with npx webpack and serving it with a local static server, the library was loading just fine.
The problem with npx webpack serve is a bug within the library webpack-dev-server and is going to be fixed in version 4.0.0 in a few days.

Vuejs 3 webpack : Problem with vue-template-compiler

I am trying to integrate vuejs 3 to an existing project which uses webpack. I read about vue-loader, so I am trying to use it.
In the official documentation I have this:
Every time a new version of vue is released, a corresponding version of vue-template-compiler is released together. The compiler's version must be in sync with the base vue package so that vue-loader produces code that is compatible with the runtime. This means every time you upgrade vue in your project, you should upgrade vue-template-compiler to match it as well.
So, when I try to compile I get this error:
Vue packages version mismatch:
- vue#3.0.2 (/home/alejo/playground/parquesFrontend/node_modules/vue/index.js)
- vue-template-compiler#2.6.12 (/home/alejo/playground/parquesFrontend/node_modules/vue-template-compiler/package.json)
This may cause things to work incorrectly. Make sure to use the same version for both.
If you are using vue-loader#>=10.0, simply update vue-template-compiler.
If you are using vue-loader#<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.
But when I try to install vue-template-compiler#3.0.2 I get this error:
❯ npm install vue-template-compiler#3.0.2
npm ERR! code ETARGET
npm ERR! notarget No matching version found for vue-template-compiler#3.0.2.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/alejo/.npm/_logs/2020-11-17T02_52_46_458Z-debug.log
How can I solve this problem?
To make vue 3 work fine with webpack without using vite or vue cli follow these steps :
init the package.json like :
{
"private": true,
"name": "vue-3",
"description": null,
}
install the last version of vue :
npm i --save vue#next vue-loader#next
install also the dev dependencies that includes #vue/compiler-sfc which replaces vue-template-compiler
npm i -D #vue/compiler-sfc css-loader file-loader mini-css-extract-plugin
url-loader webpack webpack-cli webpack-dev-server
#vue/compiler-sfc
css-loader
file-loader
mini-css-extract-plugin
url-loader
vue-loader
webpack
webpack-cli
webpack-dev-server
create or edit your webpack.config.js with following content :
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (env = {}) => ({
mode: env.prod ? "production" : "development",
devtool: env.prod ? "source-map" : "cheap-module-eval-source-map",
entry: [
require.resolve(`webpack-dev-server/client`),
path.resolve(__dirname, "./src/main.js")
].filter(Boolean),
output: {
path: path.resolve(__dirname, "./dist"),
publicPath: "/dist/"
},
resolve: {
alias: {
// this isn't technically needed, since the default `vue` entry for bundlers
// is a simple `export * from '#vue/runtime-dom`. However having this
// extra re-export somehow causes webpack to always invalidate the module
// on the first HMR update and causes the page to reload.
vue: "#vue/runtime-dom"
}
},
module: {
rules: [
{
test: /\.vue$/,
use: "vue-loader"
},
{
test: /\.png$/,
use: {
loader: "url-loader",
options: { limit: 8192 }
}
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { hmr: !env.prod }
},
"css-loader"
]
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
})
],
devServer: {
inline: true,
hot: true,
stats: "minimal",
contentBase: __dirname,
overlay: true,
injectClient: false,
disableHostCheck: true
}
});
Add dev script to run your app :
{
"private": true,
"scripts": {
"dev": "webpack-dev-server"
},
"dependencies": {
"vue": "^3.0.2"
},
"name": "vue-3",
"description": null,
"devDependencies": {
...
}
}
Fill the index.html with following content :
<link rel="stylesheet" href="/dist/main.css" />
<div id="app"></div>
<script src="/dist/main.js"></script>
Finally run npm run dev the visit http://localhost:8080/
for a ready to use project try to clone this REPOSITORY which built by following the steps above.
I believe you need to use vue-loader#next with Vue 3
In Vue 3 the SFC compiler package is no longer vue-template-compiler but compiler-sfc (check here)
I completely agree with the suggestion to use Vue CLI to manage the project - it will save you lot of trouble managing all the dependencies (especially now when Vue 3 ecosystem is trying to catch-up with Vue 3 release and lots of tool even don't have any migration documentation ....like vue-loader)
If you are not able to use CLI because your existing project already have Webpack config, you can still use CLI as a guide. Just generate new project on the side, use vue inspect command to inspect Webpack config it is using and package.json for required dependencies...
I've just installed the Webpacker gem in rails that comes with nice tasks that install Vue.
Nevertheless, it installs Vue 2.x with its loader and template compiler...
To bump everything to Vue 3 aand its dependencies simply run:
yarn add vue#next vue-loader#next #vue/compiler-sfc
Voila! You're using Vue 3 now
I upgraded a Vue2 app to Vue3 manually and I was getting this error when I was running the unit tests after upgrading all of the dependencies.
To get everything working, I also had to fix Jest's config file.
In jest.config.js set the "transform" property to:
{
transform: '^.+\\.vue$': `vue-jest`
}
The dependencies I used to get started were from a new Vue3.0 app I created using the cli. Below are the dependencies that my cli settings got me:
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/cli-plugin-eslint": "~4.5.0",
"#vue/cli-plugin-router": "~4.5.0",
"#vue/cli-plugin-unit-jest": "~4.5.0",
"#vue/cli-plugin-vuex": "~4.5.0",
"#vue/cli-service": "~4.5.0",
"#vue/compiler-sfc": "^3.0.0",
"#vue/test-utils": "^2.0.0-0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0-0",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.2",
"typescript": "~3.9.3",
"vue-jest": "^5.0.0-0"
}
Note that for my cli settings, the .eslintrc.js also has some minor changes in it for Vue3. In a fresh install the cli makes the "extends" property as below:
extends: [
`plugin:vue/vue3-essential`,
`eslint:recommended`
],

Can not access defined exports from the webpack bundle?

I'm trying to compose a webpack bundle from existing js files and use exports from that in both other JS files and occasionally in html script tag. Later add babel to transpile the whole thing to es5, hence commented out section with babel and ts, that btw works fine.
For now I am having a problem with the exports using straight webpack.
Webpack config as as follows:
var path = require('path');
const { updateCommaList } = require('typescript');
module.exports = {
entry: {
'core' : [
'./src/utils.js',
'./src/zdlg.js'
]
},
devtool: 'source-map',
stats: 'verbose',
resolve: {
modules: ['node_modules']
},
resolveLoader: {
extensions: ['.ts', '.tsx'],
mainFields: ['loader', 'main']
},
output: {
filename: '[name].js',
library: "LIB",
libraryTarget: 'var',
path: path.resolve(__dirname, "dist")
},
module: {
}
};
I can bundle files, no errors there. I'm exporting functions using export statement like so:
export function v ...
And in the html file I have
<script src="./core.js"></script>
<script type="module" src="./io.js"></script>
After tinkering for last few days I've figured out couple things.
If I have a single file in the entry section for core object, exports work, in that the LIB variable has property for each exported function and I can call LIB.v() fine.
imports do not work anyway, i.e.
io.js import:
import {v} from './core.js';
Generates an error when loading the page: Uncaught SyntaxError: import not found: v for the line above.
Adding second file to entry causes webpack to override exports from the 1st file. The reason I didn't see any exports initially was that zdlg.js wasn't exporting anything, and LIB had no exports. If zdlg.js exports any functions they are the only ones that show up on the LIB.
So, in the end, I can export functions from a single file, but I thought the whole purpose of the webpack was to allow to compose modules from multiple files.
I'm not sure what am I missing or where I am going wrong, should be very basic stuff...
In case it is important, here's dev dep list too:
"devDependencies": {
"#babel/core": "^7.8.7",
"#babel/preset-env": "^7.8.7",
"babel-loader": "^8.0.6",
"install": "^0.13.0",
"npm": "^6.14.7",
"ts-loader": "^6.2.1",
"typescript": "^3.8.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.0.9"
}
Made it work, although not sure if this is the best way.
Basically, instead of adding multiple entries in the export's entry section, I've created index.js file which reexported all the exports from the files I needed:
require('./src/ut');
require('./src/zd');
export * from './src/ut';
export * from './src/zd'
Entry is now just index.js
entry: {
'core' : 'index.js'
},
Works as intended, but I am not sure why wouldn't webpack automate this, and why would I have to export everything myself...

Poltergeist in CircleCI: TypeError: undefined is not an object (evaluating 'this.__reactAutoBindMap')

Webpacker version: 3.5.5
React/ReactDOM version: 16.6.3
Poltergeist version: 1.10.0
React-Rails version: 1.6.2
React_UJS version: 2.4.4
PROBLEM:
Running my test build on CircleCI I run into a few difrerent flavors of this --Capybara::Poltergeist::JavascriptError:
A partial look into my .circleci/config.yml
- run:
name: precompile assets (webpacker)
command: NODE_ENV=test bundle exec rake webpacker:compile
Everything in my Chrome browser using the webpack-dev-server looks as it should. Large white space after the first name is just a redacted last name.
When running the build with NODE_ENV=test I get this error in Circle...
Invariant Violation: Minified React error #31; visit https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7B%24%24typeof%2C%20type%2C%20key%2C%20ref%2C%20props%2C%20_owner%2C%20_store%7D&args[]=
(I've looked into this exception and I don't understand it in our context)
https://reactjs.org/docs/error-decoder.html/?invariant=31&args[]=object%20with%20keys%20%7B%24%24typeof%2C%20type%2C%20key%2C%20ref%2C%20props%2C%20_owner%2C%20_store%7D&args[]=
Since things spin up fine when running as dev, I decided to toggle the NODE_ENV to development to see what that turned up. The following was the error I got.
TypeError: undefined is not an object (evaluating 'this.__reactAutoBindMap')
Unfortunately there is only one SO post that I could find that mentioned anything about 'this.__reactAutoBindMap' and I didn't find anything on the post helpful at all. (TypeError: undefined is not an object (evaluating 'this.__reactAutoBindMap'))
Procfile
web: bundle exec rails s
webpacker: yarn start
package.json
{
"dependencies": {
"#babel/polyfill": "^7.0.0",
"#rails/webpacker": "3.5",
"babel-preset-react": "^6.24.1",
"caniuse-lite": "^1.0.30000697",
"jquery": "^3.3.1",
"prop-types": "^15.6.2",
"rails-erb-loader": "^5.5.0",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react_ujs": "^2.4.4",
"webpack": "^3.0.0"
},
"devDependencies": {
"#babel/cli": "^7.2.0",
"#babel/core": "^7.2.0",
"webpack-dev-server": "2.11.2"
},
"scripts": {
"start": "./bin/webpack-dev-server"
}
}
.babelrc
{
"presets": [
[
"env",
{
"modules": false,
"targets": {
"browsers": ["last 2 versions", "safari >= 7"],
"uglify": true
},
"useBuiltIns": true
}
],
"react"
],
"plugins": [
"syntax-dynamic-import",
"transform-object-rest-spread",
[
"transform-class-properties",
{
"spec": true
}
]
]
}
webpacker.yml
Note: You must restart bin/webpack-dev-server for changes to take effect
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: ['app/javascript/react_16_components']
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
extensions:
- .jsx
- .js
- .js.erb
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: false
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: /node_modules/
test:
<<: *default
compile: true
# Compile test packs to a separate directory
public_output_path: packs-test
production:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true
app/javascript/packs/application.js
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import "#babel/polyfill";
const react16Components = require.context('react_16_components', true);
const ReactRailsUJS = require('react_ujs');
const $ = require('jquery');
ReactRailsUJS.useContext(react16Components);
// Generates and exposes Rails named URLs within React components!
// -- Restarting the server as you would when adding a new Rails route should expose it to the JS world, too.
require('../rails-js-routes/js-routes');
react_test.html.haml (current_user is just a User object)
= react_component 'admin/hello', visitor: current_user
Components:
app/javascript/react_16_components/admin/hello.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Nav from '../navigation/nav'
export default class Hello extends Component {
static propTypes = {
visitor: PropTypes.object.isRequired
};
seeingRed() {
$('#color-here').css('color', 'red');
}
render() {
const { visitor } = this.props;
return(
<div style={{'textAlign': 'center'}}>
<Nav visitor={visitor.full_name} />
<div>Lets hope webpack gives us minimal trouble!</div>
<button id='color-here' onClick={this.seeingRed}>Click Here!</button>
</div>
);
}
}
app/javascript/react_16_components/navigation/nav.jsx
import React from 'react'
const Nav = ({visitor}) => (
<div><h3>{`H! ${visitor} -- I'm a stateless component navigation bar! I swear (⌐■_■)`}</h3></div>
);
export default Nav;
app/javascript/rails-js-routes/js-routes.js.erb
<%# encoding: UTF-8 %>
<%= JsRoutes.generate() %>
app/config/webpack/loaders/erb.js
module.exports = {
test: /\.erb$/,
enforce: 'pre',
exclude: /node_modules/,
use: [{
loader: 'rails-erb-loader',
options: {
runner: (/^win/.test(process.platform) ? 'ruby ' : '') + 'bin/rails runner'
}
}]
}
app/config/webpack/environment.js
const { environment } = require('#rails/webpacker');
const erb = require('./loaders/erb');
environment.loaders.append('erb', erb);
module.exports = environment;
app/config/webpack/test.js
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
CONTEXT:
We're trying to migrate our large application with lots of JS written in CoffeeScript to a new version of React. Instead of bumping the version and working through the deprecation warnings within the React-Rails gem, I'm trying to make it so that we temporarily have two versions of React running and so that each time a fellow engineer touches an old and outdated CoffeeScript file, they use react-codemod and other tools to translate the code into a new React 16 file (and put it in a new directory tree to be consumed, polyfilled/transpiled, and compiled by Webpack instead of Sprockets). I'm still not certain if this is possible, but I'm able to run things fine in local development, so there's hope (famous last words). I'm also able to render an old React 0.14 component via = react_component() using a camelCase class name on top of a React 16 component using the same helper method but with a path argument instead of a class name on the same page, without console error.
We have weird dependencies on jQuery and our Rails named routes being exposed to our React code because we use React in a rather unconventional way (inherited). We've peppered it into our app here and there and didn't just exposed a Rails API so that we could use ReactRouter and Axios (or other tooling) to render/fetch/manipulate data in a more React-ish way.
I find my little understanding and the somewhat 'magic' of Webpacker's conventions obfuscating things for me. I'd almost rather roll my own Webpack config instead of using the gem, but for the sake of hoping I made a rather dumb error (besides trying to use two versions of React) I wanted to put this out there to see if anyone could help shed some light. Super new to Babel and Webpack, so go easy on me -- but would LOVE anyones help.
Thank you in advance.

Categories

Resources