Why tree-shaking isn't working?
Handlebars is contained in the production bundle.
Since the "development" branch is eliminated when building the app and the compile function is not used at all, shouldn't the production bundle be produced without any references to Handlebar? See "SAMPLE 1", below.
In addition, even though I commented out the call on compile function, thus leaving the import unused, Handlebars is included in the production build again. See "SAMPLE 2", below.
This is the index.js file (linked into html):
import { doStuff } from './templates';
doStuff();
SAMPLE 1
This is templates.js:
import { compile } from 'handlebars';
export function doStuff() {
let string;
if (process.env.NODE_ENV === 'development') {
const raw = `<div>Hello {{name}}</div>`
const templ = compile(raw);
string = templ({ name: 'James Bond' });
} else {
string = 'IN PRODUCTION';
}
console.log({ string });
}
SAMPLE 2
import { compile } from 'handlebars';
export function doStuff() {
let string;
if (process.env.NODE_ENV === 'development') {
// const raw = `<div>Hello {{name}}</div>`
// const templ = compile(raw);
// string = templ({ name: 'James Bond' });
string = 'DEVELOPMENT';
} else {
string = 'IN PRODUCTION';
}
console.log({ string });
// function getTemplate(){}
}
And this is the build script in package.json:
"build": "parcel build public/index.html --experimental-scope-hoisting",
| Software | Version(s) |
| ---------------- | ---------- |
| Parcel | 1.12.3
| Node | 10.16.3
| npm/Yarn | 6.10.1
| Operating System | Windows 10
A good question.
Consider this code:
// index.js
import foo from "lib";
```js
// node_modules/lib/index.js
console.log("Hello!");
export default function(){
return 1234;
}
After treeshaking, the behaviour of your code must stay the same. If the `import` above was simply ignored (because the local value isn't used), the `console.log` statement would also be removed (change the behaviour).
Instead, all imports need to be considered and then we try to find unused code that can be removed (I will look into whether Parcel can be improved here).
(This is also one of the few cases where CommonJS imports are somewhat easier to optimize:
```js
if(process.env.NODE_ENV === "development") {
const handlebars = require("handlebars");
handlebars.compile(...);
} else {
...
}
@mischnic
So you imply that withing Handlebars' module there is some code that performs side effects (similar to the console.log statement in your example) while I'm just importing and not using compile function?
How do you suggest to solve this issue? By using CommonJS imports, just like the example you provided? As I'm using ES6 imports it's a little bit ugly mixing the two.
So you imply that withing Handlebars' module there is some code that performs side effects (similar to the console.log statement in your example) while I'm just importing and not using compile function?
No, there is simply no way for Parcel to know that there are no side effects.
How do you suggest to solve this issue?
The real solution is improving Parcel's tree shaking algorithm.
By using CommonJS imports, just like the example you provided
That would be a workaround that should work.
With handlebars specifically, there not much we can do, if you take a look at https://github.com/wycats/handlebars.js/blob/master/lib/handlebars.js:
it's entry point looks like this:
import AST from './handlebars/compiler/ast';
import { parser as Parser, parse } from './handlebars/compiler/base';
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
import Visitor from './handlebars/compiler/visitor';
function create() {
// ...
}
let inst = create();
export default inst;
Because of this create() call, a minifier like terser can't completely remove handlebars even if it's unused.
Most helpful comment
With handlebars specifically, there not much we can do, if you take a look at https://github.com/wycats/handlebars.js/blob/master/lib/handlebars.js:
it's entry point looks like this:
Because of this
create()call, a minifier like terser can't completely remove handlebars even if it's unused.