I have a component library written in vue that I am wrapping up with rollup
I am having an issue with mixins not being wrapped up into the final library. Intially i thought that the path was the issue as most of the mixins are local.
Originally:
import mixin from '../../mixins/color'
Repo folder structure
- dist //output
- src //All files related to the actual component within the library
- components
- comps
- alert //general components
- inputs //input components
- layout //layout components /row/col
- mixins
- utilities
- entry.js //rollup points to this
- ... //I used nuxt to develop the components to focus on SSR so there are more folders but are excluded in the rollup process
Apparently native rollup doesn't like indirect imports so I attempted to add rollup-plugin-includepaths. My understanding is that I would need to mention the paths required in the imports to work correctly.
Therefore, I added rollup-plugin-includepaths to rollup.config.js plugins and added the root path and the output director as the options
includePaths({
paths: ['src/components/', 'src/mixins/', 'src/utilities/'],
extensions: ['.js', '.vue']
}),
**this did not work **
I decided to remove all relative imports and create aliases for each required directory. This did not work either
What is happening is all mixins imported into the component and added as mixin: [mixins] //whatever they may be are not included in the compiled product?!?!?!
What am I missing????
// rollup.config.js
import fs from 'fs'
import path from 'path'
import vue from 'rollup-plugin-vue'
import alias from '#rollup/plugin-alias'
import commonjs from '#rollup/plugin-commonjs'
import replace from '#rollup/plugin-replace'
import babel from 'rollup-plugin-babel'
import { terser } from 'rollup-plugin-terser'
import minimist from 'minimist'
import postcss from 'rollup-plugin-postcss'
import includePaths from 'rollup-plugin-includepaths'
import del from 'rollup-plugin-delete'
// Get browserslist config and remove ie from es build targets
const esbrowserslist = fs
.readFileSync('./.browserslistrc')
.toString()
.split('\n')
.filter(entry => entry && entry.substring(0, 2) !== 'ie')
const argv = minimist(process.argv.slice(2))
const projectRoot = path.resolve(__dirname)
const baseConfig = {
input: 'src/entry.js',
plugins: {
preVue: [
alias({
resolve: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
entries: [
{ find: '#', replacement: path.resolve(projectRoot, 'src') },
{
find: '#mixins',
replacement: path.resolve(projectRoot, 'src', 'mixins')
},
{
find: '#comps',
replacement: path.resolve(projectRoot, 'src', 'components', 'comps')
},
{
find: '#inputs',
replacement: path.resolve(
projectRoot,
'src',
'components',
'inputs'
)
},
{
find: '#utilities',
replacement: path.resolve(projectRoot, 'src', 'utilities')
}
]
}),
includePaths({
paths: ['src/components/', 'src/mixins/', 'src/utilities/'],
extensions: ['.js', '.vue']
}),
commonjs(),
postcss()
],
replace: {
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.ES_BUILD': JSON.stringify('false')
},
vue: {
css: false,
template: {
isProduction: true
}
},
babel: {
exclude: 'node_modules/**',
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue']
}
}
}
// ESM/UMD/IIFE shared settings: externals
// Refer to https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
const external = [
// list external dependencies, exactly the way it is written in the import statement.
// eg. 'jquery'
'vue'
]
// UMD/IIFE shared settings: output.globals
// Refer to https://rollupjs.org/guide/en#output-globals for details
const globals = {
// Provide global variable names to replace your external imports
// eg. jquery: '$'
vue: 'Vue'
}
// Customize configs for individual targets
const buildFormats = []
if (!argv.format || argv.format === 'es') {
const esConfig = {
...baseConfig,
external,
output: {
compact: true,
file: 'dist/comps.esm.js',
format: 'esm',
exports: 'named'
},
plugins: [
del({ targets: 'dist/*' }),
replace({
...baseConfig.plugins.replace,
'process.env.ES_BUILD': JSON.stringify('true')
}),
...baseConfig.plugins.preVue,
vue(baseConfig.plugins.vue),
babel({
...baseConfig.plugins.babel,
presets: [
[
'#babel/preset-env',
{
targets: esbrowserslist
}
]
]
})
]
}
buildFormats.push(esConfig)
}
if (!argv.format || argv.format === 'cjs') {
const umdConfig = {
...baseConfig,
external,
output: {
compact: true,
file: 'dist/comps.ssr.js',
format: 'cjs',
name: 'Components',
exports: 'named',
globals
},
plugins: [
replace(baseConfig.plugins.replace),
...baseConfig.plugins.preVue,
vue({
...baseConfig.plugins.vue,
template: {
...baseConfig.plugins.vue.template,
optimizeSSR: true
}
}),
babel(baseConfig.plugins.babel)
]
}
buildFormats.push(umdConfig)
}
if (!argv.format || argv.format === 'iife') {
const unpkgConfig = {
...baseConfig,
external,
output: {
compact: true,
file: 'dist/comps.min.js',
format: 'iife',
name: 'Components',
exports: 'named',
globals
},
plugins: [
replace(baseConfig.plugins.replace),
...baseConfig.plugins.preVue,
vue(baseConfig.plugins.vue),
babel(baseConfig.plugins.babel),
terser({
output: {
ecma: 5
}
})
]
}
buildFormats.push(unpkgConfig)
}
// Export config
export default buildFormats
Update
I moved the imported components out of the mixin and added them directly to the component that included them and got the same result. Therefore, i really have no clue what needs to happen.
TL;DR
None of the child components are being included in the rolled up dist '.js' files
Sometimes it is hard to include what is relevant and the question above is guilty.
The problem is within the larger component I had imported the children lazily
ex:
components:{
comp: ()=>import('comp') ///Does not work
}
changed to your standard
import comp from 'comp'
components:{
comp
}
Related
I'm trying to create a react components library which is based on Typescript and SASS. The components library will be used in multiple other typescript projects, so type exports are needed as well. Ideally I want to mimic something like "Material-UI"/"React-Bootrap" libraries dist output solutions.
Example project structure:
|Tabs
+--Tabs.tsx
+--Tabs.scss
+--index.tsx
index.tsx
index.tsx
export { Tabs } from './Tabs/Tabs';
Tabs/index.tsx
import React from 'react';
import './Tabs.scss';
interface TabsProps {
...
}
export const Tabs: React.FC<TabsProps> = (props) => <div>...</div>
Tabs/index.tsx
export { Tabs } from './Tabs';
Expected built dist structure should mimic the src structure:
|Tabs
+--Tabs.js
+--Tabs.d.ts
+--index.js
+--index.d.ts
index.js
index.tsx
I tried analyzing open source projects and see how they are building the libraries, however I could not find libraries using the same approaches that I could reuse.
The solutions I've tried:
Webpack: While I could compile typescript and sass files the webpack would always emit only one file specified in the output section, which usually would be bundled and I would loose the ability to import single component from a specific component's module. I know I can specify multiple entry points, but the project will be having a lot of exports and manually specifying them is not an option...
Example config I tried:
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
// sass-loader is not used here yet, but should be once desired structure can be reached
{
test: /\.tsx?$/,
loader: 'babel-loader',
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ test: /\.js$/, loader: "source-map-loader" }
]
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
extensions: [".tsx", ".ts", ".js"],
plugins: [
new TsconfigPathsPlugin({ configFile: "./tsconfig.build.json" })
]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
Rollup: Similar situation as webpack
Example config that I tried:
// rollup.config.js
import babel from 'rollup-plugin-babel';
import sass from 'rollup-plugin-sass';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import react from 'react';
import reactDom from 'react-dom';
const babelOptions = {
exclude: /node_modules/,
// We are using #babel/plugin-transform-runtime
runtimeHelpers: true,
extensions: ['.js', '.ts', '.tsx'],
configFile: './babel.config.js',
};
const nodeOptions = {
extensions: ['.js', '.tsx', '.ts'],
};
const commonjsOptions = {
ignoreGlobal: true,
include: /node_modules/,
namedExports: {
react: Object.keys(react),
'react-dom': Object.keys(reactDom)
},
};
export default {
input: 'src/index.tsx',
output: {
name: '[name].js',
dir: 'dist',
format: 'umd',
sourcemap: true,
},
plugins: [
nodeResolve(nodeOptions),
sass(),
commonjs(commonjsOptions),
babel(babelOptions)
],
};
Babel: I managed to compile the typescript code however once I came close to transpiling SASS files I would end up with suggestions to use webpack for that...
TSC: I successfully could run the typescript compiler and it would compile all the files without problems and would maintain the same structure. However TSC does not support other transpiling options so after a lot of searches I would end up with suggestions to use webpack and "ts-loader" or "babel-loader"..
tsconfig:
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"lib": [ "es2015", "dom" ],
"outDir": "dist",
"baseUrl": ".",
"declaration": true,
"composite": true,
"module": "commonjs",
"target": "es5"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
Desirced solution:
I should be able after compiling the library and installing it in another project be able to run the following:
import { Tabs } from 'my-lib/Tabs';
import { Tabs } from 'my-lib';
After a lot of playing around I managed to produce the wanted result with rollup. The only downside of the current configuration is that it does not support newly added files in the --watch mode. The magic setting is under the output.preserveModules
Config:
// rollup.config.js
import commonjs from '#rollup/plugin-commonjs';
import typescript from '#rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
import postcssUrl from 'postcss-url';
import resolve from "#rollup/plugin-node-resolve";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
export default {
input: 'src/index.tsx',
output: {
dir: 'dist',
format: 'es',
preserveModules: true,
sourcemap: true,
},
plugins: [
resolve(),
peerDepsExternal(),
commonjs(),
typescript({
tsconfig: 'tsconfig.build.json'
}),
postcss({
minimize: true,
modules: {
generateScopedName: "[hash:base64:5]"
},
plugins: [
postcssUrl({
url: "inline"
})
]
}),
],
};
I hope this config can help others as well
You can checkout this repo. I made some changes for building a lib.
https://github.com/21paradox/react-webpack-typescript-starter
To use a library like below:
import Input from 'my-custom-ui/entry/Input';
import { Input } from 'my-custom-ui';
After doing a lot of searching, I ended up writing a plugin to manually generate the webpack entry code that webpack needed (for building a ui library).
The multiple entry + manualy generated entry file seems to be working for component seperate & no redudant code. This is also very helpful if you want to build a vue based libray.
You can clone my minimum repo https://github.com/rockmandash/webpack-chunks-question
Or see the code below
page1.js
import 'core-js';
console.log('I am page1');
page2.js
import 'core-js';
import 'react';
import 'react-dom';
console.log('I am page2');
My webpack config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entriesFilePath = [
require.resolve('./src/page1.js'),
require.resolve('./src/page2.js'),
];
const mode = 'development';
const webpackConfig = entriesFilePath.map((entrieFilePath) => {
const fileName = path.basename(entrieFilePath, path.extname(entrieFilePath));
// fileName would be page1 and page2
return {
mode,
devtool: 'cheap-module-source-map',
entry: {
[fileName]: entrieFilePath,
},
output: {
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
filename: `${fileName}.html`,
},
),
),
].filter(Boolean),
};
});
module.exports = webpackConfig; // I have to export an array, because in the future, I need to do something else.
The generated dist folder looks like this:
dist
/page1.html
/page2.html
/static
/js
/page1.js.map
/vendors~page2.chunk.js.map
/vendors~page1.chunk.js // this includes core-js !!!!
/page1.js
/page2.js
/vendors~page1.chunk.js.map
/vendors~page2.chunk.js // this includes core-js too !!!!
/page2.js.map
You see, the generated two chunks both includes core-js, how can I make my webpack config smart enough to automatically separate core-js or other common vendor files out of the box?
You do not need to import core-js anywhere. Create a .babelrc file.
{
"presets": [
[
"#babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
Instal #babel/core, #babel/polyfill, #babel/preset-env, babel-loader
Add to webpack
const optimization = {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
};
module: {rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]},
Take a look at my solution, there is a complete code. Use of core-js. He uses only part polyfil when it is needed.
https://github.com/tomik23/webpack-babel-corejs/blob/master/webpack.config.js#L17
And the second solution needed in your code is to use spliChunks
https://github.com/tomik23/photoBlog/blob/master/config/webpack.config.js#L31
P.S. If you import 'core-js' in this way; you download the whole core-js if you use my method then core-js chooses only what is needed and packages are smaller.
I have created an npm library, where I am exploring using rollup for bundling.
Here is the config:
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import { sizeSnapshot } from 'rollup-plugin-size-snapshot';
import pkg from './package.json';
const input = 'src/index.js';
const name = 'TooltipTrigger';
const globals = {
react: 'React',
'react-dom': 'ReactDOM',
'prop-types': 'PropTypes',
'react-popper': 'ReactPopper'
};
const external = id => !id.startsWith('.') && !id.startsWith('/');
const getBabelOptions = ({ useESModules = true } = {}) => ({
exclude: 'node_modules/**',
runtimeHelpers: true,
plugins: [['#babel/plugin-transform-runtime', { useESModules }]]
});
export default [
{
input,
output: {
name,
file: 'dist/react-popper-tooltip.js',
format: 'iife',
globals
},
external: Object.keys(globals),
plugins: [
resolve({
browser: true,
modulesOnly: true
}),
commonjs({
include: 'node_modules/**'
}),
babel(getBabelOptions()),
sizeSnapshot()
]
},
{
input,
output: {
name,
file: 'dist/react-popper-tooltip.min.js',
format: 'iife',
globals
},
external: Object.keys(globals),
plugins: [
resolve({
browser: true,
modulesOnly: true
}),
commonjs({
include: 'node_modules/**'
}),
babel(getBabelOptions()),
terser(),
sizeSnapshot()
]
},
{
input,
output: { file: pkg.main, format: 'cjs' },
external,
plugins: [babel(getBabelOptions({ useESModules: false })), sizeSnapshot()]
},
{
input,
output: { file: pkg.module, format: 'esm' },
external,
plugins: [babel(getBabelOptions()), sizeSnapshot()]
}
];
I want to remove prop types from prod builds in the IIFE build. The babel-plugin-transform-react-remove-prop-types removes the prop types declarations well, but since I have declared prop-types as global in rollup config, it keeps it as a dependency. When I remove it from globals, it resolves the package and bundles it inside the final build. What should I do here? Also is my build config optimal w.r.t. creating iife, cjs and esm builds?
You can use transform-react-remove-prop-types babel plugin with options removeImport: true. So this condition finally remove prop-types from build.
['transform-react-remove-prop-types', { removeImport: true }] for example.
We want to divide our large frontend projects into multiple separately deployed projects which are easier to work with. I am trying to include a bundled ngModule to handle a route from within another app. The apps must be ignorant of each other's configuration. The bundles will share some large dependencies(like Angular) via globals. We don't need to shake across the bundles and we may just have to accept some duplicate dependencies.
The root router complains that
Error: No NgModule metadata found for 'TestsetModule'.
which leads me to believe the child module is not being angular compiled on load, or is not registering its module for some reason. I think it may be necessary to manually compile the module, but I'm not sure how to use this https://angular.io/api/core/Compiler#compileModuleAndAllComponentsAsync
The root app loads the child via a route:
import { ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const load = require("little-loader");
const routes: Routes = [
{ path: ``, loadChildren: () => new Promise(function (resolve) {
load('http://localhost:3100/testset-module-bundle.js',(err: any) => {
console.log('global loaded bundle is: ', (<any>global).TestsetModule )
resolve((<any>global).TestsetModule)
}
)
})}
];
export const HostRouting: ModuleWithProviders = RouterModule.forRoot(routes);
I also tried using angular router's string resolution syntax rather than this weird global thing you see but I had similar issues.
Here is the module which is being loaded, very standard except for the global export:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { HttpModule } from '#angular/http';
//import { MaterialModule } from '#angular/material';
import { FlexLayoutModule } from '#angular/flex-layout';
import { FormsModule } from '#angular/forms';
import { LoggerModule, Level } from '#churro/ngx-log';
import { FeatureLoggerConfig } from './features/logger/services/feature-logger-config';
import { TestsetComponent } from './features/testset/testset.component';
import { TestsetRouting } from './testset.routing';
#NgModule({
imports: [
CommonModule,
//MaterialModule,
FlexLayoutModule,
HttpModule,
FormsModule,
LoggerModule.forChild({
moduleName: 'Testset',
minLevel: Level.INFO
}),
TestsetRouting,
],
declarations: [TestsetComponent],
providers: [
/* TODO: Providers go here */
]
})
class TestsetModule { }
(<any>global).TestsetModule = TestsetModule
export {TestsetModule as default, TestsetModule};
Here is the webpack configuration of the root bundle. Note the global exports via the poorly named "ProvidePlugin".
const webpack = require('webpack');
const AotPlugin = require('#ngtools/webpack').AotPlugin;
const path = require('path');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const IgnorePlugin = require('webpack/lib/IgnorePlugin');
const PolyfillsPlugin = require('webpack-polyfills-plugin');
const WebpackSystemRegister = require('webpack-system-register');
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
'bundle': './root.ts'
},
output: {
libraryTarget: 'umd',
filename: '[name].js',//"bundle.[hash].js",
chunkFilename: '[name]-chunk.js',
path: __dirname
},
externals: {
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
module: {
rules: [
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: 'raw-loader' },
]
},
devtool: '#source-map',
plugins: [
new webpack.ProvidePlugin({
'angular': '#angular/core',
'ngrouter': '#angular/router',
'ngxlog':'#churro/ngx-log'
})
]
};
config.module.rules.push(
{ test: /\.ts$/, loaders: [
'awesome-typescript-loader',
'angular-router-loader',
'angular2-template-loader',
'source-map-loader'
] }
);
}
return config;
};
And here is the webpack configuration of the child bundle. Note the "externals" which look for angular as a global.
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
'testset-module-bundle': './src/index.ts'
},
output: {
//library: 'TestsetModule',
libraryTarget: 'umd',
filename: '[name].js',//"bundle.[hash].js",
chunkFilename: '[name]-chunk.js',
path: path.resolve(__dirname, "dist")
},
externals: {
//expect these to come from the app that imported us
// name to be required : name from global
'angular': '#angular/core',
'ngrouter': '#angular/router',
'ngxlog': '#churro/ngx-log'
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
module: {
rules: [
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: 'raw-loader' },
]
},
devtool: '#source-map',
plugins: [
]
};
config.module.rules.push(
{ test: /\.ts$/, loaders: [
'awesome-typescript-loader',
'angular-router-loader',
'angular2-template-loader',
'source-map-loader'
] }
);
}
return config;
};
And for good measure here is my tsconfig file which 'awesome-typescript-loader' reads.
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"baseUrl": ".",
"rootDir": "src",
"outDir": "app",
"paths": {
"#capone/*": [
"*"
],
"#angular/*": [
"node_modules/#angular/*"
],
"rxjs/*": [
"node_modules/rxjs/*"
]
}
},
"exclude": ["node_modules", "src/node_modules", "compiled", "src/dev_wrapper_app"],
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
}
}
If you're still reading, awesome. I was able to get this working when both bundles are part of the same webpack config and the child module is just a chunk. Angular is designed to do that. But our use case is to have the children and parent be ignorant of each other until runtime.
As you have mentioned
The apps must be ignorant of each other's configuration.
I had a similar problem in Angular2. I solved it by creating a sub-application. A separate sub-main.browser.ts and index.html file. It had its own dependencies, sharing the same node modules. Both main modules bootstrapping different app-component. We were working on Angular without angular-cli.
In webpack config, I added
entry: {
'polyfills': './src/polyfills.browser.ts',
'main' . : './src/main.browser.aot.ts',
'sub-main' : '/src/sub-main.browser.ts'
},
and a more detailed HtmlWebpackPlugin. In the chunks, we load only modules that will be used in both the app. If we see polyfills is common.
new HtmlWebpackPlugin({
template: 'src/index.html',
title: METADATA.title,
chunksSortMode: 'dependency',
metadata: METADATA,
inject: 'head',
chunks: ['polyfills','main']
}),
new HtmlWebpackPlugin({
template: 'src/index2.html',
title: 'Sub app',
chunksSortMode: 'dependency',
metadata: METADATA,
inject: 'head',
filename: './sub-app.html',
chunks: ['polyfills','sub-main']
}),
The next task was to create separate endpoints for both sub apps for dev environment.
devServer: {
port: METADATA.port,
host: METADATA.host,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
},
proxy: {
"/sub-app": {
target: "http://localhost:3009",
bypass: function(req, res, proxyOptions) {
return "/index2.html";
}
}
}
},
Now when I build the project two different HTML files are generated. Each with their own javascript bundle dependencies and common assets. They can be deployed on a different server as well.
I was able to finish my POC with lots of trial and error. My suggestion will be to look a step above angular. See how webpack is deploying your current project. And If you can configure it to serve your purpose.
I;m wondering if it's possible to build a bundle with some javascript files but without dependencies?
I want to have small bundles with React components (each react component in my case is builded from few react components, for example Comment component incldues comment box, list, and form),
I can split React components to a separate files by specifying few entry points in webpack, but if I have:
1. Component comment
2. Component newsletter
and both of them require ReactDOM, files which will be generated will have like 600kb, where my react components contain only ~100 lines of js code.
I would like to have one more file which will contain all the code which would come from "require('react-dom'), and those two files which will only have the React component code. is that possible?
My current setup:
'use strict';
import path from 'path';
import CommonsChunkPlugin from "webpack/lib/optimize/CommonsChunkPlugin";
module.exports = {
entry: {
app: "./app.js",
newsletter: "./components/renderers/newsletter.renderer.js",
comment: "./components/renderers/comment.renderer.js"
},
output: {
path: path.join(__dirname),
filename: "built/[name].entry.js"
},
devtool: 'sourcemaps',
cache: true,
debug: true,
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: [/(node_modules)/],
loader: 'babel'
}
],
resolve: {
extensions: ['', '.js', '.jsx']
}
},
plugins: [
new CommonsChunkPlugin({
name: "comment.js",
chunks: ["comment", "app"],
minChunks: 2
}),
new CommonsChunkPlugin({
name: "newsletter.js",
chunks: ["newsletter", "app"],
minChunks: 2
})
]
};
Comment.renderer.js:
import CommentBox from './../comment/commentBox';
ReactDOM.render(
<CommentBox/>,
document.getElementById("comment")
);
Newsletter.renderer.js:
import Newsletter from './../newsletter/newsletter';
ReactDOM.render(
<Newsletter/>,
document.getElementById("newsletter")
);
app.js:
'use strict';
import React from 'react';
import ReactDOM from 'react-dom';
import client from './lib/client';
Ok I've managed how to do that:
import path from 'path';
import CommonsChunkPlugin from "webpack/lib/optimize/CommonsChunkPlugin";
module.exports = {
entry: {
vendor: ["react","react-dom", "underscore"],
comment: "./components/renderers/comment.renderer.js",
newsletter: "./components/renderers/newsletter.renderer.js"
},
output: {
path: path.join(__dirname),
filename: "built/[name].bundle.js"
},
devtool: 'sourcemaps',
cache: true,
debug: true,
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: [/(node_modules)/],
loader: 'babel'
}
],
resolve: {
extensions: ['', '.js', '.jsx']
}
},
plugins: [
new CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
})
]
};
this part:
minChunks: Infinity
will ensure that code included in bundle "vendor" is not included in any other bundle. Thanks to this approach, comment and newsletter will contain only my React components.
I use following code in webpack.config.js to exclude the external dependencies from bundle.
module.exports = {
...
...
externals:{
"react": "React",
"react-dom": "ReactDOM"
},
...
...
}
I found this answer from this link
To complement #AnandShanbhag's answer, you can take all the dependencies from package.json and turn them into externals with a function:
module.exports = {
...
// put everything inside package.json dependencies as externals
externals: Object.keys(require('./package.json').dependencies)
.reduce(
function (acc, cur) {
acc[cur] = cur
return acc
},
new Object()
),
...
}