react-loadable and server side rendering with webpack 3 - javascript

I am using react-loadable for react-router-v4and I want to do server side rendering.
Of course on server side I need not lazy loading, so I started to use react-loadable, because it says that it is possible to use with server with help of import-inspector babel plugin.
But unfortunately I got error in my server console require.ensure is not a function, which caused rerendering on my client side and I am losing all benefits of server-side rendering.
Before I had been using react-router-v3 and used getComponent with import without any problem.
Here is my routes config.
export default [
{
component: Root,
routes: [
{
path: '/',
component: Loadable({
loader: () => import('./Parent.jsx'),
loading: () => <div>Loading...</div>
}),
routes: [
{
path: '/',
exact: true,
component: Loadable({
loader: () => import('./Child.jsx'),
loading: () => <div>Loading...</div>
})
}
]
}
]
}
];
This is my .babelrc
{
presets : [["es2015", {modules: false}], "react", "stage-2", "stage-3"],
plugins: [
"transform-runtime",
"syntax-async-functions",
"dynamic-import-webpack"
],
env: {
node: {
plugins: [
["import-inspector", {
"serverSideRequirePath": true,
"webpackRequireWeakId": true,
}],
["babel-plugin-webpack-alias", {
"config": "webpack/server.js",
"findConfig": true
}]
]
}
}
}
On client it works perfectly, only error is markup checksum diffing error.
How is this supposed to work?
Thanks in advance!

Try add this to babel or babel loader
"dynamic-import-node",
That is from airbnb

Related

Vue 3, vite vue.esm-bundler.js build breaks jQuery

So I'm using vite to build my Vue 3 application for a legacy website which still uses jQuery and a few other JS frameworks.
I'm using the esm bundler as I would still like to boot it up and use it with slotted components.
<div id="app">
<vue-component-name></vue-component-name>
</div>
And it works perfectly. But when jQuery is used on the page, no where near my components it seems the esm bundled version of Vue has set a global variable named $ which breaks jQuery.
Has anyone had this issue or know of a way to fix it?
import { defineConfig } from 'vite';
import type { UserConfig as VitestUserConfigInterface } from 'vitest/config';
import svgLoader from 'vite-svg-loader';
import vue from '#vitejs/plugin-vue';
import path from 'path';
const vitestConfig : VitestUserConfigInterface = {
test: {
globals: true,
include: ['./tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
};
export default defineConfig({
test: vitestConfig.test,
plugins: [vue(), svgLoader()],
base: '/',
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
'#': path.resolve(__dirname, '/src'),
},
},
build: {
outDir: '../wwwroot/dist',
emptyOutDir: true,
manifest: true,
rollupOptions: {
input: {
main: './src/main.ts',
},
output: {
entryFileNames: 'assets/js/[name].js',
chunkFileNames: 'assets/js/[name].js',
assetFileNames: ({ name }) => {
if (/\.(gif|jpe?g|png|svg)$/.test(name ?? '')) {
return 'assets/images/[name][extname]';
}
if ((name ?? '').endsWith('.css')) {
return 'assets/css/[name][extname]';
}
return 'assets/[name][extname]';
},
globals: {
vue: 'Vue',
},
},
},
},
server: {
hmr: {
protocol: 'ws',
},
},
});
EDIT:
More information, I've tracked this down to using
#input="handleInput($event.target, index)"
This right here breaks existing jQuery. Still no idea how to get around it
For anyone interested, How to wrap Vite build in IIFE and still have all the dependencies bundled into a single file?

Rollup library importing file with named and default export not working

I have a rollup library that is trying to use Antd react components. Without external component libraries, everything works fine. If I add the component library (material ui complains of a similar issue), it complains about trying to import variables that are not exported while importing the component library.
rollup.config.js:
export default [
{
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
plugins: [visualizer({ open: useVisualizer })],
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
],
external: ['react', 'react-dom'],
plugins: [
resolve({
preferBuiltins: true
}),
commonjs({
transformMixedEsModules: true,
requireReturnsDefault: 'auto',
esmExternals: true,
// dynamicRequireTargets: ['node_modules/antd/es/**/*.js']
}),
typescript({ tsconfig: './tsconfig.json' }),
folder(),
postcss({
plugins: [],
}),
eslint(),
terser(),
],
},
{
input: './src/index.ts',
output: [{ file: 'dist/index.d.ts', format: 'esm' }],
plugins: [dts()],
external: [/\.css$/],
},
];
Error message:
[!] Error: 'Group' is not exported by node_modules/antd/es/radio/radio.js, imported by node_modules/antd/es/calendar/Header.js
However, when we look at the exports from radio.js, 'Group' is clearly exported from radio/index.js from within the radio directory:
import Group from './group';
import InternalRadio from './radio';
import Button from './radioButton';
export { Button, Group };
var Radio = InternalRadio;
Radio.Button = Button;
Radio.Group = Group;
Radio.__ANT_RADIO = true;
export default Radio;
Header.js imports this like so: import { Button, Group } from '../radio';, so the import SHOULD be going to radio/index.js, however, it appears it is actually going to radio/radio.js instead. No amount of rollup configuration has made this change so far. Any ideas how to get this to resolve to index.js instead of radio.js?
Turns out the normal node-resolve plugin does not resolve "local" imports (i.e. file system paths instead of package paths). The solution here is to also use rollup-plugin-local-resolve, which does resolve these paths.

rollup-plugin-babel not recognizing JSX

I am having issues with RollupJS API. For some reason, its not recognizing JSX syntax. Rollup (API) is giving me this error.
Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
2: import App from "./App";
3: const MyApp = (props) => {
4: return <View index={<App />} url={props.url} serverOptions={props.serverOptions}/>;
^
5: };
6: export default MyApp;
Here's the Rollup config.
export const loadBuildConfigurationOptions = (tsconfigOptions: object, root: Path = Process.Cwd()): RollupOptions => {
return <RollupOptions> {
input: Path.FromSegments(root, "src", "index.ts").toString(),
output: [
{
dir: Path.FromSegments(root, "dist").toString(),
format: "cjs"
}
],
external: [
"solid-js",
"solid-js/web",
"solidus"
],
plugins: [
typescript(tsconfigOptions),
nodeResolve({
preferBuiltins: true,
exportConditions: ["solid"],
extensions: [".js", ".jsx", ".ts", ".tsx"]
}),
nodePolyfill(),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]],
}),
json(),
styles(),
copy({
targets: [
{ src: 'src/assets/**/*', dest: 'dist/src/assets' }
]
}),
],
preserveEntrySignatures: false,
treeshake: true,
};
}
I am using this in a build tool for a SolidJS SSR library I am developing. what is happening here is this function generates the appropriate Rollup configuration file to for the project being build by the user of this library. The returned Rollup configuration object is then being used by the Rollup JS API to build the actual application. Do I need to change the parameters in the babel plugin or something?
Okay. I figured it out. It’s quite silly really. I just had to make one change to the Babel plugin.
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]],
extensions: [“.js”, “.ts”, “.jsx”, “.tsx”],
}),
Adding the extensions option seemed to have fixed everything.

Rollup config for React component library not working with SSR

I'm trying to set up a React component library using Rollup, which my React app then installs and uses. The consuming React application is being rendered on the server. I have managed to get this set up working with Webpack, wherein I'm bundling my React component library into one single file and sending it down. Problem is this is a huge file and negates the point of SSR (part of it at least) as it takes a long time to download.
So I decided to use Rollup to split up the components into individual ES modules which are then pulled in and used as required by the app. The problem is that I can't get this to work with SSR at all. This is my first time using Rollup so I may have missed something obvious. Here is my rollup.config.js
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import css from 'rollup-plugin-css-only';
import alias from '#rollup/plugin-alias';
import image from '#rollup/plugin-image';
import json from '#rollup/plugin-json';
import replace from '#rollup/plugin-replace';
import namedDeps from './named-dependencies';
import stylus from './rollup-plugin-stylus-v2';
import postcssUrl from './rollup-plugin-postcss-url';
import autoExternal from 'rollup-plugin-auto-external';
import url from '#rollup/plugin-url';
import fs from 'fs';
const namedDepsObject = namedDeps
.map(depPath => ({
keys: Object.keys(require(depPath)).filter(v => v !== 'default'),
name: depPath,
}))
.reduce((acc, val) => {
acc[val.name] = val.keys;
return acc;
}, {});
let ComponentsList = require('./components-file'); // This has an array of component src and name
const rollConfigObject = fileName => {
const configOpts = {
input: `${fileName.src}`,
output: [
{
file: `dist/ssr/esm/${fileName.name}.js`,
format: 'esm',
}
],
plugins: [
autoExternal(),
url({
include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif', '**/*.woff', '**/*.woff2', '**/*.eot', '**/*.ttf', ],
emitFiles: false
}),
babel({
exclude: 'node_modules/**',
presets: ['#babel/env', '#babel/react'],
plugins: ['#babel/plugin-syntax-dynamic-import'],
runtimeHelpers: true,
}),
resolve(),
commonjs({
sourceMap: false,
namedExports: {
...namedDepsObject,
},
}),
image(),
json(),
stylus(),
postcssUrl(),
css({
output: function(styles, styleNodes){
fs.appendFileSync(__dirname + '/dist/ssr/styles.css', styles);
}
}),
],
};
return { ...configOpts };
};
export default ComponentsList.map(
moduleName => {
return rollConfigObject(moduleName);
}
)
Now this creates the separate component bundles like I want, but the problem is that it seems to be including code within the createCommonjsModule methods from node_modules which is then injecting browser objects like document, which when I attempt to render on the server throws an error.
I am trying to not include node_modules at all, so when I try to run this with only the babel plugin, it throws an Error: Could not resolve entry module and refuses to build. An example is Braintree, it's adding a lot of code with document usage.
I'm not sure how to get it to not inject node_modules code within the component. In the app, I'm using Webpack to run these modules through babel, but objects like document will pass through and then refuse to work on the server.
Any suggestions would be great here, been struggling with this for a number of days now. Thanks!
I got the config to work after I updated my babel setting to
babel({
exclude: [
'../../node_modules/**',
'../node_modules/**',
'node_modules/**',
],
presets: [
[
'#babel/env',
{
modules: false,
targets: {
esmodules: true,
},
},
],
'#babel/react',
],
plugins: [
'#babel/plugin-proposal-object-rest-spread',
'#babel/plugin-transform-runtime',
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
],
runtimeHelpers: true,
}),
commonjs({ sourceMap: false }),

Failed to mount component: template or render function not defined - vue warning when using Laravel-mix

The problem
I created the frontend of a project in Vue2. I used a plugin called 'vue-svg-inline-loader' to convert my svg images to inline svg code. In order to get the script to work I had to add this config code to my vue.config.js.
The team has now implemented the frontend on the backend (Laravel) using Laravel-mix. This broke my "vue-svg-inline-loader" as it does not have the config anymore to figure out what to target.
Code that I've tried
I read a lot of questions and answers on StackOverflow but this kind of configuration seem so vast I cannot seem to put my finger on how shoul I write it. I believe that from my attempt, I am missing the rule(vue) from my previous file and the use("vue-svg-inline-loader") which was pointing to the plugin.
My previous vue.config.js which was working
module.exports = {
css: {
loaderOptions: {
sass: {
data: `#import "#/css/all.scss";`
}
}
},
chainWebpack: config => {
config.module
.rule("vue")
.use("vue-svg-inline-loader")
.loader("vue-svg-inline-loader")
.options({ /* ... */ });
}
}
My attempt at webpack.mix.js
mix.webpackConfig({
module : {
rules : [
'vue',
{
use : [
'vue-svg-inline-loader',
{
loader: 'vue-svg-inline-loader',
options : {}
},
]
},
]
},
});
Updates
Update 26 Aug: I managed to get in touch with the dev of vue-svg-inline-loader. We worked on some leads and managed to get to this:
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.copy('resources/assets', 'public/assets', true)
.webpackConfig({
module: {
rules: [
{
use: [
{
loader: "vue-svg-inline-loader",
options: {
inline: {
keyword: "svg-inline",
strict: true
}
}
}
]
}
]
}
});
With this it loads all the SVGs properly but it does not load any img that uses an image in a different format than SVG. I thought it's a rule or a test problem, therefore I added test: /\.vue$/,. With this I encountered errors of missing vue-loader so I added vue-loader as well. With vue-loader declared, the code compiled. When I accessed the app only a blank page returned along with the following console error:
[Vue warn]: Failed to mount component: template or render function not defined.
The final, not working code looks like:
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.copy('resources/assets', 'public/assets', true)
.webpackConfig({
module: {
rules: [
{
test: /\.vue$/,
use: [
{
loader: "vue-loader",
options: { /* ... */ }
},
{
loader: "vue-svg-inline-loader",
options: {
inline: {
keyword: "svg-inline",
strict: true
}
}
}
]
}
]
}
});
Managed to get to a solution along with the Laravel Mix team. I have also informed the developer of vue-svg-inline-loader and this solution should be now featured in their documentation.
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.copy('resources/assets', 'public/assets', true)
.override(config => {
config.module.rules.push({
test: /\.vue$/,
use: [{ loader: 'vue-svg-inline-loader' }]
})
});

Categories