Slide 1

Slide 1 text

Managing "side effect" in Frontend Development ⚡️ @Frontend Conference Okinawa

Slide 2

Slide 2 text

$ who am i Web Developer at Nikkei GitHub: @Shinyaigeek Twitter: @Shinyaigeek Love: Web, OSS, Beer, Cooking, others... Member of pnpm Shinobu Hayashi/ 林 仁 ` ` ` `

Slide 3

Slide 3 text

$ What is "side effect" ? > In computer science, an operation, function or expression is said to have a side effect if it modifies some state variable value(s) outside its local environment, which is to say if it has any observable effect other than its primary effect of returning a value to the invoker of the operation from: wikipedia expression: file-system I/O, stdio, etc... function: an effect to outside of the function's scope ✅ This talk is aimed on a function’s "side effect" in Frontend context

Slide 4

Slide 4 text

$ Example of a function’s side effects readArticle function have an effect on counter variable which is located in outside of readArticle let counter = 0; const readArticle = function (id) { counter++ // ❗️ return `${some nice article accord with id}`; } ` ` ` ` ` `

Slide 5

Slide 5 text

$ Why Should We Care About Side Effect In Frontend Development? To keep function simple, of course To eliminate unnecessary code from built output JavaScript ✅ This talk is aimed to built JavaScript

Slide 6

Slide 6 text

$ Why Eliminate Unnecessary Code from Built JavaScript? lead to less bundle size, more UX! prevent danger code from leaking internal code for developing environment-specific code (like server, browser)

Slide 7

Slide 7 text

$ Frontend Build-Toolchain Features for Code Optimization Minify Dead Code Elimination Tree Shaking 👈 etc...

Slide 8

Slide 8 text

$ What is Tree Shaking? A. The function to eliminate unused modules from bundle. Bundler will eliminate getCurrentDay from bundle because it is not used and have no effect on bundled code // module.ts export getCurrentMonth = (date: Date = new Date()) => { return date.getMonth(); } export getCurrentDay = (date: Date = new Date()) => { return date.getDay(); } // app.ts import { getCurrentMonth } from './module.ts'; someNiceFunction(getCurrentMonth()); ` `

Slide 9

Slide 9 text

$The Impact of Side Effects in Frontend Development For example, what output bundler will generate from the above input… Along with ECMAScript’s specification, ESModule should be evaluated overall when it is imported from others. // config.ts const defineConfig = (config) => { return { version: 1, ...config } } export const configA = defineConfig({ name: "A" }) export const configB = defineConfig({ name: "B" }) export const configForTest = defineConfig({ name: "Testing", debug: "some scare secret" }) // app.ts import { configB } from "./config" console.log(configB)

Slide 10

Slide 10 text

$ How to accomplish better tree-shaking Bundler "Because I don’t know whether defineConfig function have side effect or not…" Output: const defineConfig = (config) => { return { version: 1, ...config } }; defineConfig({ name: "A" }); // Why ❗️❓ app does not use this ❗️ const configB = defineConfig({ name: "B" }); defineConfig({ name: "Testing", debug: "some scare secret" }); // Why ❗️❓ app does not use this ❗️ console.log(configB); ` `

Slide 11

Slide 11 text

$ Eliminating ESModule’s Side Effects from the Bundle Bundler "Because I don’t know whether defineConfig function have side effect or not…" defineConfig in the example is pure, but perhaps defineConfig may have side effect like below : Human can recognize defineConfig is pure easily, but Bundler cannot recognize defineConfig is pure or with side effect ` ` ` ` ` ` let version = 0; const defineConfig = (config) => { version += 1; return { version, ...config } }; ` ` ` `

Slide 12

Slide 12 text

$ ESModule’s Side Effect with Module Bundler Module Bundler will bundle also side effect Module Bundler cannot find the facing module's exported member is pure or not

Slide 13

Slide 13 text

Eliminating ESModule’s Side Effects from the Bundle example with terser: terser --compress pure_funcs=[defineConfig] We can eliminate ESModule’s side effect from bundle by specializing defineConfig as a pure function ` ` ` ` const defineConfig = (config) => { return { version: 1, ...config } }; defineConfig({ name: "A" }); // 👈 Terser can remove this because defineConfig is pure and this invoke does not const configB = defineConfig({ name: "B" }); defineConfig({ name: "Testing", debug: "some scare secret" }); // 👈 Terser can remove this because defineConfi console.log(configB);

Slide 14

Slide 14 text

$ How to accomplish better tree-shaking We can handle this with the implicit pure functions specification but we cannot detect this easily...

Slide 15

Slide 15 text

$ Treeche, Tree-Shakable Checker Treeche will throw alert for the input such the former example Treeche is the tool to detect the code which is not tree-shakable in ESModules. 🚨 /~~~/config.ts is not tree-shakable due to the following code: defineConfig({ ^^^^^^^^^^^^^^ name: "A" ^^^^^^^^^^^ }); ^^^ defineConfig({ ^^^^^^^^^^^^^^ name: "Testing", debug: "some scare secret" ^^^^^^^^^^^ }); ^^^ Creator of Treeche ✌️ 👉

Slide 16

Slide 16 text

$ How Treeche works A. Treeche bundles each input modules internally on memory. Treeche generate JavaScript code like above, and bundle this with rollup by resolving memory prefixed module path as specified input modules. And then, Treeche checks the built output and throw a pretty alert if there are the code other than ImportSpecifier because it indicates this modules is not tree-shakable. Feature Native TypeScript support for input Checking the multiple files with Node.js glob import * from "memory:specified modules"; ` ` ` `

Slide 17

Slide 17 text

$ Summary Frontend devs should care about ESModule's side effect to reduce bundle size to prevent some dangerous code from leaking treeche is useful to detect side effects in modules