How to load a webpack React bundle with SystemJS? - javascript

I have a main React application and I create webpack bundled React libraries that can be loaded dynamically in the main app upon user request or as a result of some user actions.
I thought using SystemJS to dynamically load the librairies was the way to go but I can't find any pointers on the topic.
Here is my issue:
If I bundle the library with React dependencies, I can load the file with SystemJS but I'm getting this error:
invariant.js:44 Uncaught (in promise) Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded
This makes sense since React as been included in my bundle and is also part of the main App. So I thought excluding React from the lib will solve the problem. I added the "external" to my webpack config:
///////////////////////////////////////////////////////////
// Webpack config development
//
///////////////////////////////////////////////////////////
module.exports = {
devtool: 'source-map',
context: path.join(
__dirname, '../src/Extensions'),
entry: {
Extension: [
'./Extension/index.js'
]
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: "[name].js",
libraryTarget: "umd",
library: "[name]"
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
WEBPACK: true
}
}),
new webpack.ProvidePlugin({
'window.jQuery': 'jquery',
jQuery: 'jquery',
_: 'lodash',
$: 'jquery',
React: "React",
react: "React",
"window.react": "React",
"window.React": "React"
}),
new ProgressBarPlugin({
format: ' build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)',
clear: false
})
],
resolve: {
modules: [
path.resolve('./node_modules'),
],
extensions : ['.js', '.jsx', '.json']
},
resolveLoader: {
modules: ['node_modules']
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['transform-runtime']
}
}]
}
]
},
externals: {
"react": "React",
"react-dom": "ReactDOM"
}
}
But after doing so, loading the lib with SystemJS will output the following error:
ConfiguratorView.js:116 Uncaught (in promise) Error: (SystemJS) Unexpected token <
SyntaxError: Unexpected token <
at eval ()
Evaluating http://localhost:3000/React
Error loading http://localhost:3000/Extension.js
at eval ()
I understand SystemJS is attempting to load the React dependency at localhost:3000/React and from what I read this has to be configured with SystemJS.config({...}) but the question is how?? I read the SystemJS config documentation, but I haven't seen any example like so.
Am I the only one trying to dynamically load a React library? Is there a better approach? I want to have a flexible mechanism in place so unecessary libraries can be loaded on demand and do not bloat the main bundle.
Thanks for any pointer on that

Use a debundler or webpack as a browserify plugin to map the package names and locations to the SystemJS configuration files:
SystemJS is meant to work with any module definition specifications, and even a repository URL. This means when SystemJS encounters a require('module_name') or an import module_name, it doesn't know where to find the module called module_name. Would it be on npm? Or is it a custom repository? SystemJS can't know for sure.
So we'd need to provide a mapping of all packages' names and the location of their repository. Needless to say, doing this manually is impractical, so we must use another package manager, that manages packages from everywhere.
References
Build a React Application from First Principles - Modules + SystemJS
Stack update 2016: Future-proofing our javascript bundling with webpack 2 and rollup | Signal Agency Linz
How to bundle Syncfusion ReactJS components using webpack and browserify? | JavaScript | Syncfusion
shama/webpackify: Use webpack through a Browserify plugin
1egoman/debundle: A javascript debundler. Takes a Browserify or Webpack bundle and recreates the initial, pre-bundled source.

Related

PhpStorm not recognizing webpack alias

My webpack.config.js contains the following part:
resolve: {
extensions: ['*', '.js', '.ts', '.tsx'],
modules: [
path.resolve(__dirname, "src"),
path.resolve(__dirname, "node_modules"),
],
alias: {
Design: path.resolve(__dirname, "src/Theme/Default")
}
},
There are 2 folders in this src/Theme folder: Default & Dark. I want to switch it in the alias setting when needed, it's not user controlled.
The import I use:
import Page from 'Design/Components/Page';
Webpack is working correctly with this but PhpStorm 2018.2 does not recognize this path as correct.
My PhpStorm settings for webpack are set to the path of my webpack.config.js.
More details and examples / logs can be found here: https://youtrack.jetbrains.com/issue/WI-43146
Webpack aliases resolving is not supported when editing TypeScript files, current TypeScript support implementation uses only the TypeScript resolution logic because we need to keep integration with the TypeScript language service (that is not aware of webpack aliases).
You can configure similar mappings in your tsconfig.json file, like:
"baseUrl": "",
"paths": {
"Design/*": ["src/Theme/Default/*"]
}
If you miss support for webpack resolving in TypeScript, please vote for WEB-29207

How to Webpack a multiple page (tsx) website with separate js files?

I am using:
react for page rendering.
typescript directly(without babel) transpiling tsx to ES5 for browsers
webpack to generate codes separately
babel-polyfill to be packaged in the vendor.js but not referenced by typescript codes
I want to package javascript libraries and my common codes into one vendor.js and generate separately [name].js files with [name].html (with template) referencing both vendor.js and [name].js for each [name].tsx (which conatins only a class definition [name] that extends React.Component<P,S>) with HtmlWebpackPlugin.
[name].tsx may import modules. Modules included in vendor.js cannot be included in [name].js, and the others must be normally included in [name].js
I have read the document form webpack official site and still have no idea how to do it.
I did find some guides that realize a multiple page website but having all libraries included in the separate [name].js files, which means the javascript libraries are redundantly included in every single [name].js and it is surely not expected.
And I found some guides about externals, but it can only be used on existing js files, not generated bundle files.
Here is my current webpack.config.js which supports only single page:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
"index.js": ['babel-polyfill', './src/index.tsx']
},
output: { filename: '[name]' },
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
module: {
rules: [{
test: /\.(ts|tsx)$/,
loader: 'ts-loader',
exclude: '/node_modules/'
}, {
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader'
},]
},
resolve: {
extensions: ['.webpack.js', '.web.js', '.ts', '.js', '.tsx']
},
devtool: "source-map"
}
Is there any way to have it support multiple pages as described?

javascript + gulp + babel + webpack. error: Module not found: Error: Can't resolve 'babel-loader'

I'm creating a javascript project. To create it I'm using gulp and babel.
My problem is that I can't develop my code over multiple file, so I'm search a solution to 'enable' importing. At the moment I'm trying to configure webpack.
The Gulp Task is this:
gulp.task('webpack', () => {
return webpack_stream(webpack_config)
.pipe(rename('webpack_code.js'))
.pipe(gulp.dest('.build/asset/webpack/'));
});
The webpack.config.js is this:
module.exports = {
entry: ['./src/asset/js/main.js'],
output: {
filename: 'bundle.js',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: [
['env', 'stage-0',{ modules: false }],
],
},
},
],
},
resolveLoader: {
modules: ['./node_modules'],
},
resolve: {
modules: ['./node_modules'],
},
target: 'node',
};
My current error is this:
Error in plugin 'webpack-stream'
Message:
multi ./src/asset/js/main.js
Module not found: Error: Can't resolve 'babel-loader' in ...
What's wrong?
Another Question: What's I have to put as value of entry key? Only the entry point js file or the whole files of the project?
Thanks!!
What's wrong?
I'd guess that in your project, your Webpack instance is not finding the babel loader because of your config / environment specific issues.
I've had the exact same issue as you. Here are some troubleshooting steps for to check first:
See if babel-loader is actually installed. I know it is simple, but it can save you time.
Check which Webpack/Babel versions you're dealling with in your package.json file. I'm using Webpack 4 and Babel 8. Sounds like some newer versions doesn't accept this: use: 'babel' in your webpack.config file. You need to ensure that the -loader is being used as it follows: use: 'babel-loader'.
Reinstall your node_modules folder. Sometimes it works.
Another Question:
What's I have to put as value of entry key?
Only the entry point js file or the whole files of the project?
Accordingly to Webpack's docs:
The entry object is where webpack looks to start building the bundle. The context is an absolute string to the directory that contains the entry files. - Webpack Ref
Considering that, you should pass to the entry object, the path of a folder or a file that will be used to generate your final JS file with all your modules in it.
If you have nested files, that you don't import as modules, I think you'll have to head to the docs and see this specific case.
But if this files are nested and are being imported as modules, in your entry file/folder, they will be generated in the output file.
I know it's not much but following these steps, helped me to solve it. :)

Webpack - Using Script Loader in webpack.config.json

I am just starting to dip my toes into the world of webpack. I am using the awesome Vue.js with vueify, so therefore my modules are ES6.
One difficulty I am having is loading some 3rd party jQuery plugins. I am using the ProvidePlugin to load jQuery - which works fine.
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
I then have a directory called plugins containing misc jQuery plugins. My understanding is the script loader just loads these into the bundled file as strings, and they are eval-ed when the bundle loads. These scripts can then be used as if they were loaded in a regular script tag (i.e., no import needed).
But I just cant get any of the plugins to work. Below is my loaders array. What I am doing wrong (or not doing)?
loaders: [
// process *.vue files using vue-loader
{
test: /\.vue$/,
loader: 'vue'
},
// process *.js files using babel-loader
// the exclude pattern is important so that we don't
// apply babel transform to all the dependencies!
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
},
{
test: /plugins\.js$/,
loader: 'script-loader' #tried script too
}
]
I can sympathize with the difficulty of getting jQuery plugins to work with webpack. While I don't have a solution to this specific configuration, I have found it useful to use a cdn to keep development rolling along until further troubleshooting can be done. Below is an example.
In your .html template file:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
In index.js or whatever your main entry point is:
import $ from 'jquery'
In your webpack config:
externals: {
jquery: 'jQuery'
}
Since this approach involves direct use of script tags it may work more reliably, while temporarily sacrificing opportunities for optimization and bundling.
new webpack.ProvidePlugin({
'React': path.resolve(__dirname, "node_modules/react/react"),
'ReactDOM': path.resolve(__dirname, "node_modules/react-dom/dist/react-dom"),
"jQuery": path.resolve(__dirname, "node_modules/jquery/dist/jquery"),
"$": path.resolve(__dirname, "node_modules/jquery/dist/jquery")
})
resolve you lib path

How should I use moment-timezone with webpack?

In using webpack to build my project, I typically require modules in CommonJS from npm modules. I need moment-timezone in my project, however in building the package you must also build all the data from moment-timezone, which can be quite a lot.
Additionally the build is failing with the following error:
ERROR in ./~/moment-timezone/data/packed/latest.json
Module parse failed: /site/node_modules/moment-timezone/data/packed/latest.json Line 2: Unexpected token :
You may need an appropriate loader to handle this file type.
| {
| "version": "2015a",
| "zones": [
| "Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q",
# ./~/moment-timezone/index.js 4:15-51
At this point I am not as concerned with the build failing, as I am about the size of the build if it actually succeeds. Though, obviously the failing build will need to be addressed too at some point.
I would appreciate any pointers on how to handle this, especially if any of you have encountered this same issue using webpack (or browserify too, probably).
You can fix this by adding the JSON loader to your webpack configuration.
$npm install json-loader
And add it to your loaders in webpack.config.js. Don't forget to add the extension as well.
{
module: {
loaders: [
{include: /\.json$/, loaders: ["json-loader"]}
]
},
resolve: {
extensions: ['', '.json', '.jsx', '.js']
}
}
If you're using webpack 2.x (currently in beta)
npm install json-loader
then include this in your rules
{
test: /\.json$/,
loader: "json-loader"
}

Categories

Resources