I have setup entries in webpack config as
entry: {
// main: './src/index.js',
analytics: './src/analytics/index.js',
};
since the main entry is commented out I expected some modules not to be included in the bundle but were still being added. It turns out both entries eventually import some stuff from a file that re-exports a lot of the unwanted and wanted modules.
Why aren't such unused exports excluded from the bundle?
After going through the tree-shaking documentation I can confirm that
We use ES2015 module syntax (i.e. import and export).
#babel/preset-env is configured with modules: false, other babel presets we use are #babel/preset-react and #babel/preset-typescript
not sure if sideEffects should be set in package.json as we are not publishing the project as package, I am okay with third-party libraries not being tree-shaken. optimization.sideEffects should be true by default.
production mode is enabled
Related
I have multiple projects in react in a monorepo: app1 app2 app3, and each app is built with webpack and babel.
I want to create a shared directory with shared components between the apps, so I created in the monorepo another directory shared.
( I executed in each project npm i ../shared )
But when I import componets from shared inside one the of the apps, babel throws an exception: Add #babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation. If you want to leave it as-is, add #babel/plugin-syntax-jsx (https://git.io/vb4yA) to the 'plugins' section to enable parsing.
NOTE: if I import simple functions from shared it works, only when importing components it does not work.
What configuration can I add so I can create a shared directory with shared components?
The .babelrc file I have in each app looks like this
{
"presets": [
[
"#babel/preset-env",
{
"targets": {
"browsers": ["chrome >= 50"]
},
"useBuiltIns": "usage",
"corejs": 3
}
],
"#babel/preset-react",
"#babel/preset-typescript"
],
"plugins": ["#babel/plugin-syntax-jsx", "#babel/plugin-transform-react-jsx", "#babel/plugin-transform-runtime"]
}
It appears, this is more of a webpack and less of a babel problem. The error pops up, because your babel-loader is not configured correctly.
When adding "external folders/modules" to your build, you usually want to do (or at least check) the following:
Add the shared folder's src folder to your babel-loader's include (if it is not already included)
-> This makes sure, things get babel'ed nicely, and your preset-react will actually work).
-> Make sure not to include too much (e.g. never include any node_modules folder in its entirety, or things will slow down abysmally).
You might also have to add the shared folder to your babel-loader's babelrcRoots, because else, it will ignore its .babelrc.js file (if it is different from your babel-loader's default config), and thus only use default settings, ignoring your preset-react.
Often times, you also need to add the shared folder's src folder to your webpack.config's resolve.modules:
-> This allows webpack to do it's resolve magic inside that folder as well.
If it has its own node_modules, also add that to resolve.modules.
For more info on this particular issue, see here.
You can create a link to an external package:
Installing a local module using npm
Another possibility if you are working in a team is the more sophisticated approach:
Get npm module via Git
As for the declarations of the plugins, see documentation (you need a "plugins" array, see link):
babel js plugins
Good evening!
I have been trying for a few days to get tree shaking between different packages to work.
Before going further, I have created a minimum repro that I will explain throughout this post: https://github.com/Apidcloud/tree-shaking-webpack
I have also opened an issue on webpack repo: https://github.com/webpack/webpack/issues/8951
For simplicity sake, the example just uses webpack. Babel is not used.
The above example has two packages, both with their respective bundle:
core - exports 2 functions, cube and unusedFn
consumer - imports cube from core and exports its own function, consumerFn
Core package
Note that square function is not exported in the index.js file. It's a way to know that tree shaking is indeed working within core at least, as it's not included in the final bundle (which is correct).
Consumer package
As you can see, only cube is being imported from core. It then exports its own function (consumerFn) consuming cube.
Problem
The problem is that the consumer bundle is including everything from the core bundle. That is, it's including unusedFn when it shouldn't, resulting in a bigger bundle.
Ultimately, the goal is to do the same in a monorepo with multiple packages. There's no point on having them if each package is bundling the everything from the others. The goal is to bundle only what's necessary for each package.
Using optimizationBailout I can see that ModuleConcatenation plugin is issuing some warning messages. I also used --verbose flag:
Here's my webpack.config.js:
const path = require('path');
module.exports = {
mode: 'production',
entry: {
core: './src/index.js',
consumer: './consumer/index.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
// for simplicity sake I removed the UMD specifics here.
// the problem is the same, with or without it.
},
optimization: {
usedExports: true,
sideEffects: true
},
stats: {
// Examine all modules
maxModules: Infinity,
// Display bailout reasons
optimizationBailout: true
}
};
I also have "sideEffects": false in the package.json.
I went through webpack's guide too, but I'm not sure what is missing.
Related issues:
webpack-3-babel-and-tree-shaking-not-working
webpack-including-unused-exports-in-final-bundle-not-tree-shaking
Either I don't understand dependencies vs. devDependencies in node 100% yet or eslint is just wrong here (not capable of analyzing this correctly):
3:1 error 'chai' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
4:1 error 'chai-enzyme' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
5:1 error 'enzyme' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
7:1 error 'sinon' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
9:1 error 'redux-mock-store' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
These are test dependencies, so why is it saying that they should be listed in dependencies?
Additional note: We're using Travis as our CI so I don't know if it makes a difference for that at all either.
Solved it with adding this to my .eslintrc:
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
[no-extraneous-dependencies] Add exceptions? #422
Based on this user's reply:
you could set the option devDependencies: true in an .eslintrc in your
test folder:
rules: import/no-extraneous-dependencies: [error, { devDependencies:
true }] Then you'll get reports of any packages referenced that are
not included dependencies or devDependencies. Then you get the
goodness of the rule, with no noise from the disable comments.
I think that might work for you? This is how I would use the rule, in
your case, since you have your test code separated into a test
directory.
Also this post was helpful to confirm I wasn't insane to not want some of these in my dependencies list: Sharable ESLint Config
If you want to allow imports of devDependencies in test files only you can use an array of globs, as the documentation of no-extraneous-dependencies states:
When using an array of globs, the setting will be set to true (no errors reported) if the name of the file being linted matches a single glob in the array, and false otherwise.
The following setting will disable the lint for test files only.
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.ts", "**/*.test.tsx"]}]
That way imports from devDependencies are still reported as errors.
I was able to solve it by adding the missing packages (in my case, Typescript and Storybook) to my plugins directory in .eslintrc.
I give the specifics in this post: ESLint error: '#storybook/react' should be listed in the project's dependencies, not devDependencies
I fixed it by using
'import/no-extraneous-dependencies': [
'error',
{
projectDependencies: false,
},
],
Currently I can do:
require('./frontend/src/components/SomeComponent');
But if I set the following in my webpack.config.js:
resolve: {
root: path.resolve('frontend', 'src')
}
I can instead do:
require('components/SomeComponent');
The problem is, when I don't use Webpack (eg. in a test environment) all of my imports break. According to the Babel docs, the sourceRoot property sets the "root from which all sources are relative." This made me think I could add the following to my .babelrc to fix my imports:
"sourceRoot": "frontend/src"
... but no such luck. When I do require('components/SomeComponent'); in babel-node it fails. When I just use Babel to transpile the file, the require line is the same whether or not I set a sourceRoot.
So, my question is, is there any way (with or without sourceRoot) to simulate webpack's resolve.root in Babel?
P.S. I know there are several Babel plug-ins which address this problem, but all of the ones I've seen require you to add a ~ to the require path (which of course breaks imports in Webpack).
Many project have webpack + babel, and in many projects you sometimes bypass webpack (as in your case - for tests).
In such cases, all the resolve aliases should live in babel.
There are plugins out there to allow one reading the configuration of the other (and similar plugins for eslint etc.).
Our project is using the webpack resolve.root option to import modules with absolute paths. (avoiding something like ../../../module)
In its current state the project is using babel-loader which works perfectly fine.
My task is to migrate the app to Angular 2.
Therefor I am currently in the process of transitioning to TypeScript.
Somehow it seems like the ts-loader does not work in combination with the resolve.root option of the webpack config.
Example of the webpack.config.js
resolve: {
root: [
path.resolve('./node_modules'),
path.resolve('./app'),
path.resolve('./app/lib'),
]
},
Example of a module import
import AbstractListState from 'states/abstract_list_state';
The states directory is inside the app/lib directory.
Error when executing webpack
ERROR in ./app/mainViews/panel/panel.controller.ts
Module not found: Error: Cannot resolve module 'states/abstract_list_state' in C:\Users\...\Project\app\mainViews\panel
# ./app/mainViews/panel/panel.controller.ts 4:28-65
Pre version 2.0 TypeScript will try to load modules with an absolute path from the node_modules directory. This is because TypeScript's module resultion is per default set to "node". Which means it works like node's require method. So, even if you're using webpack to build your app, TypeScript (and its compiler) will still want to load the files.
In order to let webpack import your modules with absolute path you have to go back and use the require method. This way TypeScript will let webpack import stuff. But of course you will not get any type-inference, autocomplete, ...
Or, you update to the TypeScript 2.0 beta and give this a try: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#module-resolution-enhancements-baseurl-path-mapping-rootdirs-and-tracing