WeAreDevelopers World Congress 2024
Francesco Novy, July 19th 2024
hello@fnovy.com
mydea
How can we avoid to ship code to users for features they are not even using?
With Tree Shaking!
<html>
<body>
<script src="index.js" type="module"></script>
</body>
</html>
import { add } from './math.js';
console.log(add(1, 2));
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
index.html
index.js
math.js
How do bundlers tree shake?
With Static Analysis!
Only static code can be tree shaken!
import { add, subtract } from './math.js';
export function addOrSubtract(shouldAdd) {
if (shouldAdd) {
console.log(add(1, 2));
} else {
console.log(subtract(1, 2));
}
}
import { add, subtract } from './math.js';
// const is static!
const SHOULD_ADD = true;
export function addOrSubtract() {
if (SHOULD_ADD) {
console.log(add(1, 2));
} else {
console.log(subtract(1, 2));
}
}
import { add } from './math.js';
export function addOrSubtract() {
console.log(add(1, 2));
}
bundled to
const IS_DEBUG = false;
function runCalculation() {
if (IS_DEBUG) {
console.log('calculation started');
}
const res = 1 + 2;
return expensiveCalculation(res);
}
function runCalculation() {
console.log('calculation started');
return expensiveCalculation(3);
}
IS_DEBUG=true
function runCalculation() {
return expensiveCalculation(3);
}
IS_DEBUG=false
npx size-limit
module.exports = [
{
name: '@sentry/browser (incl. Tracing)',
path: 'packages/browser/build/npm/esm/index.js',
import: '{ init, browserTracingIntegration }',
gzip: true,
},
{
name: '@sentry/browser',
path: 'packages/browser/build/npm/esm/index.js',
import: '{ init }',
gzip: true,
},
];
.size-limit.js
import { getUser, getParent } from './user.js';
export function printName(userId, printParentName) {
if (printParentName) {
console.log(getParent(userId).name);
} else {
console.log(getUser(userId).name);
}
}
import { getUser, getParent } from './user.js';
export function printName(user) {
console.log(user.name);
}
printName(getUser(userId));
printName(getParent(userId));
Using options - not tree shakeable 🚫
Using composition - tree shakeable ✅
// SDK
import {
CanvasManager
} from './canvas-manager';
export function record(options) {
if (options.recordCanvas) {
new CanvasManager();
}
}
// Application
import { record } from 'sdk';
record({ recordCanvas: false });
Using options - not tree shakeable 🚫
// SDK
import {
CanvasManager
} from './canvas-manager';
export function getCanvasManager() {
return new CanvasManager();
}
export function record(options) {
if (options.getCanvasManager) {
options.getCanvasManager();
}
}
Using composition - tree shakeable ✅
import {
record
} from 'sdk';
record({ getCanvasManager: undefined });
import {
record,
getCanvasManager
} from 'sdk';
record({ getCanvasManager });
Without using Canvas
With Canvas
const IS_DEBUG = __SENTRY_DEBUG__;
function doSomething() {
if (IS_DEBUG) {
console.log("Log some debug info here!");
}
}
const webpack = require("webpack");
module.exports = {
// ... other options
plugins: [
new webpack.DefinePlugin({
__SENTRY_DEBUG__: !!process.env.DEBUG,
}),
],
};
import {
replaceCodePlugin
} from "vite-plugin-replace";
module.exports = mergeConfig(config, {
plugins: [
replaceCodePlugin({
replacements: [
{
from: "__SENTRY_DEBUG__",
to: !!process.env.DEBUG,
},
],
}),
],
});
Webpack
Vite
const IS_DEBUG = __SENTRY_DEBUG__;
function doSomething() {
if (IS_DEBUG) {
console.log("Log some debug info here!");
}
}
const IS_DEBUG = false;
function doSomething() {
if (IS_DEBUG) {
console.log("Log some debug info here!");
}
}
const IS_DEBUG = false;
function doSomething() {
if (IS_DEBUG) {
console.log("Log some debug info here!");
}
}
function doSomething() {
}
hello@fnovy.com
mydea
Francesco Novy