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

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?

Related

How to speed up Vue.js command line processes and optimize a webpack build

I have a very simple tutorial project that I built which consists of no more than 100-200 lines of code.
When I build this project with webpack I end up with a bundle.js file which is being flagged as being above the recommended size of a bundle.js file. I find this unsettling because I know that my code is very small. How is it that with only using a few things like vuex, vue.js and a few node modules ending up with such an oversized bundle.js?
I understand that it packages everything up for us, but I find it hard to believe that with such a small project webpack would be unable to get it down to a much smaller size. I am concerned that this might have something to do with the sheer number of node modules I have in that project root directory.
So my question is this: does the webpack build depend at all on what node-modules are in my directory under the /node_modules/ folder? If not, then how have I already exceeded the recommended size for a bundle.js with my first ever vue project?
This brings me to another question which I have been very unsure of: Is it normal for vue to copy over almost my entire node_modules directory from my root user directory? When I watch tutorials, the "vue create My_App" command seems to finish executing in no more than 10-20 seconds, but when I run the command it can take minutes. When I was wondering what it could be I saw that it copied hundreds and hundreds of node_modules over... is that entirely necessary? Is there a configuration or setting I should have set or changed that I missed?
Thank you all for any insight you might be willing to offer, big or small.
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
},
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// this will apply to both plain `.js` files
// AND `<script>` blocks in `.vue` files
{
test: /\.js$/,
loader: 'babel-loader'
},
// this will apply to both plain `.css` files
// AND `<style>` blocks in `.vue` files
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
plugins: [
// make sure to include the plugin for the magic
new VueLoaderPlugin(),
],
optimization: {
minimizer: [new UglifyJsPlugin()],
},
};
Use tools like https://nx.dev/
You can find video here https://youtu.be/mVKMse-gFBI

Configure Webpack output bundle

Im new to React development with Webpack having set up a boilerplate by following this tutorial article.
I get the jist of how webpack works and can follow along with the article but having trouble understanding how my particular webpack.config.js is creating the files and bundles that it does and how to modify these to achieve some custom function.
webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
})
],
mode: 'development'
};
I understand that the rules object grabs all .js files (excluding those in node_modules) and compiles them to standard JS using babel. Then it grabs all the .html files and creates an html file including the bundle using html-loader. The HtmlWebPackPlugin is implemented here. Mode indicates to create a development version of the bundle.
My folder structure is as follows:
- /dist (generated by webpack)
- index.html
- main.js (webpack bundle)
- /src (created manually)
- /components
- components.js (react components)
- index.html
- index.js
- .babelrc
- package.json (npm init -y)
- webpack.config.js (manually configured)
Questions:
Webpack creates the /dist directory and the files contained in it. Where in the configuration is it indicated that this directory be named "dist" and the main bundle file be named main.js?
Where is it indicated that /dist be at the root of the project. Suppose I wanted the directory to be named foo and placed two levels up (../../foo/)
In webpack.config.js the "rules" object has keys defined as test to indicate which files types to bundle. test: /\.js$/, & test: /\.html$/ Is "test" an arbitrary value or a default webpack configuration key.
I was looking at these docs with regard to webpack configuration but the syntax was very different from what is shown here.
1 & 2:
you can set output config like this:
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
filename
name of output bundle file. default: main.js
path
location of save output files. default: /dist
to get root of project you have to use path!
import path:
var path = require('path');
and use like this:
path.resolve(__dirname, DIRECTORY_NAME)
3:
test is default webpack configuration key and its to specify that loader file type support like html-loader.

Webpack: Style tag per required css file

I want to have one tag per required .css file.
I want it like that because I want to connect chrome dev-tools workspace feature to my src folder, so I could edit my css files directly from the browser.
Here's my research on loaders:
style-loader only loads into style tags
style-loader/url + file-loader doesn't work (I tried the README example)
extract-text-webpack-plugin seems to only generate ONE bundle per ALL css files with default configuration.
The Modify Files section in extract-text-webpack-plugin suggests that with multiple entry points, it's possible to generate multiple bundles, so I thought that it might be possible to abuse this feature to get the behaviour I want.
This is of course for development and I don't intend on serving my css this way.
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}
]
},
plugins: [
new ExtractTextPlugin('[name].css'),
new HtmlWebpackPlugin({
template: 'path/to/your/index.html',
})
],
resolve: {
extensions: [ '.js', '.css' ]
}
Then in your js files, do import path/to/your/stylesheet.css; for each stylesheet you want webpack to extract.
Note: you need to install html-webpack-plugin and add it like above so that webpack can insert references to your stylesheets. Click HERE to learn more about the options for HtmlWebpackPlugin

How to load a webpack React bundle with SystemJS?

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.

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

Categories

Resources