Michele Riva
Senior Software Architect @NearForm
Google Developer Expert
Microsoft MVP
MicheleRivaCode
Build scalable, high performances and modern web applications using Next.js, the React framework for production
MicheleRivaCode
*but it shouldn't
MicheleRivaCode
to change a computer program into a machine language
Cambridge Dictionary
MicheleRivaCode
to translate a source code into a different language source code
Me, myself
MicheleRivaCode
to encapsulate code and resources into a single executable file
Me, myself
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
Scala.js
https://www.scala-js.org
MicheleRivaCode
ReasonML
ReScript
F# (via Fable)
Gleam
Elm
Kotlin
Nim, Haxe, C/C++ (via Emscripten), ClojureScript, Dart, PureScript, Haskell (via GHCJS)
MicheleRivaCode
MicheleRivaCode
LLVM
MicheleRivaCode
function toUpper(x) {
return x.toUpperCase();
}
function addExclamationMarks(x) {
return x + '!!!'
}
function scream(input) {
return input
|> toUpper
|> addExclamationMarks
|> console.log
}
scream('Hello, Serbia');
// HELLO, SERBIA!!!
"use strict";
function toUpper(x) {
return x.toUpperCase();
}
function addExclamationMarks(x) {
return x + '!!!';
}
function scream(input) {
var _ref, _ref2, _input;
return _ref = (
_ref2 = (_input = input, toUpper(_input)),
addExclamationMarks(_ref2)
), console.log(_ref);
}
scream('Hello, Serbia');
// HELLO, SERBIA!!!
MicheleRivaCode
import { VFC } from 'react';
enum UserType {
ADMIN = "admin",
EDITOR = "editor",
USER = "user",
ANONYMOUS = "guest"
}
type MyProps = {
userType: UserType;
}
const MyComponent: VFC<MyProps> = ({ userType }) => {
return (
<div>
User is of type: {props.userType}
</div>
)
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var UserType;
(function (UserType) {
UserType["ADMIN"] = "admin";
UserType["EDITOR"] = "editor";
UserType["USER"] = "user";
UserType["ANONYMOUS"] = "guest";
})(UserType || (UserType = {}));
const MyComponent = ({ userType }) => {
return /*#__PURE__*/ React.createElement(
"div",
null,
"User is of type: ",
props.userType
);
};
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
ReadArticle.jsx
NewArticle.jsx
AuthorProfile.jsx
BundledPage.js
MicheleRivaCode
in depths
MicheleRivaCode
ClojureScript
(defn simple-component []
[:div
[:p "I am a component!"]
[:p.someclass
"I have " [:strong "bold"]
[:span {:style {:color "red"}} " and red "] "text."]])
cljs.user.simple_component = (function cljs$user$simple_component(){
return new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Keyword(null,"div","div",(1057191632)),
new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Keyword(null,"p","p",(151049309)),"I am a component!"], null),
new cljs.core.PersistentVector(null, 5, 5, cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Keyword(null,"p.someclass","p.someclass",(-1904646929)),
"I have ",new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Keyword(null,"strong","strong",(269529000)),"bold"], null),
new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Keyword(null,"span","span",(1394872991)),
new cljs.core.PersistentArrayMap(null, 1, [new cljs.core.Keyword(null,"style","style",(-496642736)),
new cljs.core.PersistentArrayMap(null, 1, [
new cljs.core.Keyword(null,"color","color",(1011675173)),"red"]
, null)], null)," and red "], null),"text."], null)], null);
});
https://reagent-project.github.io
MicheleRivaCode
[@bs.config {jsx: 3}];
module Greeting = {
[@react.component]
let make = () => {
<button> {React.string("Hello!")} </button>
};
};
ReactDOMRe.renderToElementWithId(<Greeting />, "preview");
// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
'use strict';
var React = require("react");
var ReactDOMRe = require("./stdlib/reactDOMRe.js");
function _none_$Greeting(Props) {
return React.createElement("button", undefined, "Hello!");
}
var Greeting = {
make: _none_$Greeting
};
ReactDOMRe.renderToElementWithId(React.createElement(_none_$Greeting, { }), "preview");
exports.Greeting = Greeting;
/* Not a pure module */
https://reasonml.github.io
ReasonML
MicheleRivaCode
TypeScript
import { VFC } from 'react';
enum UserType {
ADMIN = "admin",
EDITOR = "editor",
USER = "user",
ANONYMOUS = "guest"
}
type MyProps = {
userType: UserType;
}
const MyComponent: VFC<MyProps> = ({ userType }) => {
return (
<div>
User is of type: {props.userType}
</div>
)
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var UserType;
(function (UserType) {
UserType["ADMIN"] = "admin";
UserType["EDITOR"] = "editor";
UserType["USER"] = "user";
UserType["ANONYMOUS"] = "guest";
})(UserType || (UserType = {}));
const MyComponent = ({ userType }) => {
return /*#__PURE__*/ React.createElement(
"div",
null,
"User is of type: ",
props.userType
);
};
MicheleRivaCode
TypeScript TSC
Fable
ClojureScript
BuckleScript
JavaScript (Babel)
MicheleRivaCode
transpilation time
Really fast
Average
Fast
Slow on large codebases
Slow on large codebases
MicheleRivaCode
optimized output
Beautifully optimized
Awful
Beautifully optimized
Quite optimized
Well optimized*
MicheleRivaCode
Quite slow, quite optimized
Quite fast, well optimized*
MicheleRivaCode
Quite slow, quite optimized
Quite fast, well optimized
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
Parsing
Transformation
Codegen
MicheleRivaCode
Step 1
Tokenization
var foo = 10
var
foo
=
10
input
tokens
MicheleRivaCode
Step 2
Syntactic Analysis
var foo = 10
=
/ \
/ \
var 10
|
foo
input
parse tree
(aka concrete syntax tree)
MicheleRivaCode
Step 2
Syntactic Analysis
var foo = 10
=
/ \
/ \
var 10
|
foo
input
variableDeclaration
|
|
vairiableDeclarator
/ \
/ \
Identifier NumericLiteral
abstract syntax tree (AST)
MicheleRivaCode
MicheleRivaCode
{
"body":[
{
"type":"VariableDeclaration",
"declarations":[
{
"type":"VariableDeclarator",
"id":{
"type":"Identifier",
"name":"foo",
"loc":{
"identifierName":"foo"
}
},
"init":{
"type":"NumericLiteral",
"extra":{
"rawValue":10,
"raw":"10"
},
"value":10
}
}
],
"kind":"var"
}
]
}
MicheleRivaCode
MicheleRivaCode
export default function () {
return {
visitor: {
VariableDeclaration(path) {
console.log({ VariableDeclaration: path.node });
// { type: "VariableDeclaration", kind: "var", ... }
},
Identifier(path) {
console.log({ Identifier: path.node });
// { type: "Identifier", name: "foo", ... }
},
NumericLiteral(path) {
console.log({ NumericLiteral: path.node });
// { type: "NumericLiteral", value: 10, ... }
}
}
};
}
Babel example implementing the visitor pattern
MicheleRivaCode
export default function() {
return {
visitor: {
VariableDeclaration(path) {
if (path.node.kind === "var") {
path.node.kind = "let"
}
}
}
};
}
Babel example
MicheleRivaCode
Babel example
input
output
var foo = 10
const bar = true
let foo = 10;
const bar = true;
MicheleRivaCode
ESLint
jscodeshift
Prettier
MicheleRivaCode
ESLint
export default function(context) {
return {
TemplateLiteral(node) {
context.report({
node,
message: 'Do not use template literals',
fix(fixer) {
if (node.expressions.length) {
// Can't auto-fix template literal with expressions
return;
}
return [
fixer.replaceTextRange([node.start, node.start + 1], '"'),
fixer.replaceTextRange([node.end - 1, node.end], '"'),
];
},
});
}
};
};
Example code taken from astexplorer.net
MicheleRivaCode
ESLint
const myString = `Hello, World!`;
const myString = "Hello, World!";
input
output
MicheleRivaCode
ttypescript (https://github.com/cevek/ttypescript)
export default function (program) {
const checker = program.getTypeChecker();
return (context) => {
return (sourceFile) => {
const visitor = (node) => {
// This branch evaluates '2 + 2' like expressions and replaces the node with the result (in this case '4')
if (ts.isBinaryExpression(node)) {
if (ts.isNumericLiteral(node.left) && ts.isNumericLiteral(node.right)) {
// We could parse `node.text` as a number, or we can use the typechecker to get type info for nodes
const lhs = checker.getTypeAtLocation(node.left);
const rhs = checker.getTypeAtLocation(node.right);
switch (node.operatorToken.kind) {
case ts.SyntaxKind.PlusToken:
return context.factory.createNumericLiteral(lhs.value + rhs.value);
}
}
}
//
if (ts.isIdentifier(node) && node.text === 'printTips' || node.text === 'tips') {
return context.factory.createIdentifier(node.text.split('').reverse().join(''));
}
return ts.visitEachChild(node, visitor, context);
};
return ts.visitNode(sourceFile, visitor);
};
};
};
Example code taken from https://github.com/cevek/ttypescript#program
MicheleRivaCode
ttypescript (https://github.com/cevek/ttypescript)
input
output
const mySum = 10 + 20;
const mySum = 30;
MicheleRivaCode
export const parser = "babel";
export default function transformer(file, api) {
const j = api.jscodeshift;
return j(file.source)
.find(j.Identifier)
.forEach((path) => {
if (j(path).get().value.name.includes("oldName")) {
j(path).replaceWith(
j.identifier(path.node.name.replace("oldName", "newName"))
);
}
})
.toSource();
}
jscodeshift
MicheleRivaCode
jscodeshift
const oldNameFactory = () => {/* */};
const newNameFactory = () => {/* */};
input
output
MicheleRivaCode
input
output
import MyHeader from 'components/MyHeader';
export function MyApp(props) {
return (
<div>
<MyHeader {...props.headerProps} />
<p> Hello, {props.name}!</p>
</div>
)
}
export function MyApp(props) {
return (
<div>
<p> Hello, {props.name}!</p>
</div>
)
}
MicheleRivaCode
export const parser = 'babel'
export default function transformer(file, api) {
const j = api.jscodeshift;
const withoutElement = j(file.source)
.find(j.JSXElement)
.forEach(function (path) {
if (path.value.openingElement.name.name === "MyHeader") {
path.prune();
}
})
.toSource();
const withoutImport = j(withoutElement)
.find(j.ImportDefaultSpecifier)
.forEach(function (path) {
if (path.value.local.name === "MyHeader") {
path.parentPath.parentPath.prune();
}
})
.toSource();
return withoutImport;
};
MicheleRivaCode
const user = {
data: {
name: {
first: "Michele",
last: "Riva"
}
}
}
const middleName = user.data
&& user.data.name
&& user.data.name.middle
|| "No middle name";
console.log(middleName);
Node 12.x
No middle name
MicheleRivaCode
const user = {
data: {
name: {
first: "Michele",
last: "Riva"
}
}
}
const middleName = user.data?.name?.middle ?? "No middle name";
console.log(middleName);
Node 12.x
SyntaxError: Unexpected token '.'
MicheleRivaCode
const user = {
data: {
name: {
first: "Michele",
last: "Riva"
}
}
}
const middleName = user.data?.name?.middle ?? "No middle name";
console.log(middleName);
Node >14.x
"No middle name"
MicheleRivaCode
import get from 'lodash/get';
const user = {
data: {
name: {
first: "Michele",
last: "Riva"
}
}
}
const middleName = get(user, "data.name.middle", "No middle name");
console.log(middleName);
"No middle name"
MicheleRivaCode
import reverse from 'lodash/reverse';
import isString from 'lodash/isString';
import isFunction from 'lodash/isFunction';
import isNull from 'lodash/isNull';
import split from 'lodash/split';
import filter from 'lodash/filter';
import map from 'lodash/map';
import keys from 'lodash/keys';
// and so on...
MicheleRivaCode
import reverse from 'lodash/reverse';
// Array.reverse()
import isString from 'lodash/isString';
// typeof
import isFunction from 'lodash/isFunction';
// typeof
import isNull from 'lodash/isNull';
// typeof
import split from 'lodash/split';
// Array.split()
import filter from 'lodash/filter';
// Array.filter
import map from 'lodash/map';
// Array.map
import keys from 'lodash/keys';
// Object.keys
// and so on...
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
in depths
WebPack
Parcel
Rollup
MicheleRivaCode
Hard
Easier
Easiest
WebPack
Parcel
Rollup
MicheleRivaCode
Hard
Easier
Easiest
Slowest
Slower
Fast
WebPack
Parcel
Rollup
MicheleRivaCode
MicheleRivaCode
Is it still worth it?
MicheleRivaCode
ESBuild
SWC
Vite
Snowpack
is there any better alternative?
(rest in pepperoni)
MicheleRivaCode
esbuild src/myEntry.js --bundle --sourcemap --minify --outfile=dist/mybundle.js
ESBuild
MicheleRivaCode
MicheleRivaCode
ES2019
ES2020
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false,
"dynamicImport": false,
"privateMethod": false,
"functionBind": false,
"exportDefaultFrom": false,
"exportNamespaceFrom": false,
"decorators": false,
"decoratorsBeforeExport": false,
"topLevelAwait": false,
"importMeta": false
},
"transform": null,
"target": "es5",
"loose": false,
"externalHelpers": false,
// Requires v1.2.50 or upper and requires target to be es2016 or upper.
"keepClassNames": false
}
}
MicheleRivaCode
SWC can run in a browser thanks to WASM
MicheleRivaCode
Vite
MicheleRivaCode
Vite
MicheleRivaCode
Vite
MicheleRivaCode
// ESM
export default function greet() {
return "Hello Serbia!";
}
// ESM
import foo, { bar } from "foobar";
// CJS
module.exports = function greet() {
return "Hello Serbia!";
}
// CJS
const foo, { bar } = require("foobar");
MicheleRivaCode
Vite
MicheleRivaCode
MicheleRivaCode
rest in pepperoni
Snowpack
MicheleRivaCode
Snowpack
rest in pepperoni
MicheleRivaCode
Snowpack
rest in pepperoni
MicheleRivaCode
MicheleRivaCode
/*
* Skypack CDN - canvas-confetti@1.4.0
*
* Learn more:
* 📙 Package Documentation: https://www.skypack.dev/view/canvas-confetti
* 📘 Skypack Documentation: https://www.skypack.dev/docs
*
* Pinned URL: (Optimized for Production)
* ▶️ Normal: https://cdn.skypack.dev/pin/canvas-confetti@v1.4.0-POmgSMO0U5q84otJfYlN/mode=imports/optimized/canvas-confetti.js
* ⏩ Minified: https://cdn.skypack.dev/pin/canvas-confetti@v1.4.0-POmgSMO0U5q84otJfYlN/mode=imports,min/optimized/canvas-confetti.js
*
*/
// Browser-Optimized Imports (Don't directly import the URLs below in your application!)
export * from '/-/canvas-confetti@v1.4.0-POmgSMO0U5q84otJfYlN/dist=es2020,mode=imports/optimized/canvas-confetti.js';
export {default} from '/-/canvas-confetti@v1.4.0-POmgSMO0U5q84otJfYlN/dist=es2020,mode=imports/optimized/canvas-confetti.js';
MicheleRivaCode
MicheleRivaCode
The future is no-bundle
MicheleRivaCode
The future is no-bundle*
*maybe
MicheleRivaCode
The future is bright
MicheleRivaCode
https://kdy1.dev/posts/2022/1/tsc-go
MicheleRivaCode
MicheleRivaCode
MicheleRivaCode
@MicheleRiva
@MicheleRivaCode
/in/MicheleRiva95
www.micheleriva.dev