From 6337bf5bb9cad44a6399cb6ab1b45aa47525e73a Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Thu, 2 Jul 2020 19:54:34 -0400
Subject: [PATCH 01/26] update dependencies
---
website/package.json | 42 ++--
website/webpack.tsx | 5 +-
website/yarn.lock | 489 +++++++++++++++++++++++++------------------
3 files changed, 306 insertions(+), 230 deletions(-)
diff --git a/website/package.json b/website/package.json
index 73929069d..3f81c6a41 100644
--- a/website/package.json
+++ b/website/package.json
@@ -6,17 +6,17 @@
"deploy": "gh-pages -d dist"
},
"dependencies": {
- "@bikeshaving/crank": "*",
+ "@bikeshaving/crank": "0.2.1",
"@repeaterjs/repeater": "^3.0.1",
- "@types/fs-extra": "^8.0.1",
- "@types/webpack": "^4.41.6",
+ "@types/fs-extra": "^9.0.1",
+ "@types/webpack": "^4.41.18",
"core-js": "^3.6.4",
- "css-loader": "^3.4.2",
- "file-loader": "^5.1.0",
- "front-matter": "^3.1.0",
- "fs-extra": "^8.1.0",
- "html-loader": "^0.5.5",
- "marked": "^0.8.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "front-matter": "^4.0.2",
+ "fs-extra": "^9.0.1",
+ "html-loader": "^1.1.0",
+ "marked": "^1.1.0",
"mini-css-extract-plugin": "^0.9.0",
"normalize.css": "^8.0.1",
"postcss-loader": "^3.0.0",
@@ -24,24 +24,24 @@
"postcss-preset-env": "^6.7.0",
"prismjs": "^1.19.0",
"regenerator-runtime": "^0.13.3",
- "style-loader": "^1.1.3",
+ "style-loader": "^1.2.1",
"truncate-html": "^1.0.3",
- "ts-loader": "^6.2.1",
- "url-loader": "^3.0.0",
- "webpack": "^4.41.6"
+ "ts-loader": "^7.0.5",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0"
},
"devDependencies": {
- "@babel/core": "^7.8.4",
+ "@babel/core": "^7.10.4",
"@types/babel__standalone": "^7.1.0",
- "@types/codemirror": "^0.0.84",
- "@types/marked": "^0.7.2",
+ "@types/codemirror": "^0.0.96",
+ "@types/marked": "^1.1.0",
"@types/mini-css-extract-plugin": "^0.9.1",
- "@types/prismjs": "^1.16.0",
- "gh-pages": "^2.2.0",
+ "@types/prismjs": "^1.16.1",
+ "gh-pages": "^3.1.0",
"gh-pages-deploy": "^0.5.1",
- "nodemon": "^2.0.2",
- "ts-node": "^8.6.2",
- "typescript": "^3.7.5"
+ "nodemon": "^2.0.4",
+ "ts-node": "^8.10.2",
+ "typescript": "^3.9.6"
},
"postcss": {}
}
diff --git a/website/webpack.tsx b/website/webpack.tsx
index 3422ebe08..b92d7c547 100644
--- a/website/webpack.tsx
+++ b/website/webpack.tsx
@@ -12,7 +12,8 @@ import postcssNested from "postcss-nested";
const config: webpack.Configuration = {
mode: "development",
- plugins: [new MiniCssExtractPlugin({filename: "[name].css"})],
+ // WHY TYPESCRIPT
+ plugins: [new MiniCssExtractPlugin({filename: "[name].css"}) as any],
module: {
rules: [
{
@@ -190,7 +191,7 @@ export class Storage {
}
}
-const StorageKey = Symbol("webpack.StorageKey");
+const StorageKey = Symbol.for("CrankWebpackStorageKey");
export interface PageProps {
storage: Storage;
diff --git a/website/yarn.lock b/website/yarn.lock
index 512cec9b4..ce6d33a7a 100644
--- a/website/yarn.lock
+++ b/website/yarn.lock
@@ -9,7 +9,7 @@
dependencies:
"@babel/highlight" "^7.10.4"
-"@babel/core@^7.1.0", "@babel/core@^7.8.4":
+"@babel/core@^7.1.0", "@babel/core@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330"
integrity sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w==
@@ -176,7 +176,7 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
-"@bikeshaving/crank@*":
+"@bikeshaving/crank@0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@bikeshaving/crank/-/crank-0.2.1.tgz#b7de2eede6f295208f941c371f118dfc801360ec"
integrity sha512-yvlrHku3oN189Wd2a8Wmau8306ENlJ9QdOcHbvXo0vkUBI/g8Q8G8OT0QgaeSLvpDeKErgdrYU9+KSUpqWsBOw==
@@ -222,10 +222,10 @@
dependencies:
"@types/node" "*"
-"@types/codemirror@^0.0.84":
- version "0.0.84"
- resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.84.tgz#b0cfca79ccdfd45ffe1f737668276a31b3149ebd"
- integrity sha512-W78ZhfHPGYoYGCpAcEa268QUU3CVMA8BwcybxUMhEs2v5Rj58/7lGeRh3P7tauWRnSg3Pyn5ymw2lt65AyrVUw==
+"@types/codemirror@^0.0.96":
+ version "0.0.96"
+ resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.96.tgz#73b52e784a246cebef31d544fef45ea764de5bad"
+ integrity sha512-GTswEV26Bl1byRxpD3sKd1rT2AISr0rK9ImlJgEzfvqhcVWeu4xQKFQI6UgSC95NT5swNG4st/oRMeGVZgPj9w==
dependencies:
"@types/tern" "*"
@@ -239,10 +239,10 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
-"@types/fs-extra@^8.0.1":
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.1.tgz#1e49f22d09aa46e19b51c0b013cb63d0d923a068"
- integrity sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==
+"@types/fs-extra@^9.0.1":
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
+ integrity sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg==
dependencies:
"@types/node" "*"
@@ -251,10 +251,10 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
-"@types/marked@^0.7.2":
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.7.4.tgz#607685669bb1bbde2300bc58ba43486cbbee1f0a"
- integrity sha512-fdg0NO4qpuHWtZk6dASgsrBggY+8N4dWthl1bAQG9ceKUNKFjqpHaDKCAhRUI6y8vavG7hLSJ4YBwJtZyZEXqw==
+"@types/marked@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@types/marked/-/marked-1.1.0.tgz#53509b5f127e0c05c19176fcf1d743a41e00ff19"
+ integrity sha512-j8XXj6/l9kFvCwMyVqozznqpd/nk80krrW+QiIJN60Uu9gX5Pvn4/qPJ2YngQrR3QREPwmrE1f9/EWKVTFzoEw==
"@types/mini-css-extract-plugin@^0.9.1":
version "0.9.1"
@@ -264,11 +264,11 @@
"@types/webpack" "*"
"@types/node@*":
- version "14.0.24"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.24.tgz#b0f86f58564fa02a28b68f8b55d4cdec42e3b9d6"
- integrity sha512-btt/oNOiDWcSuI721MdL8VQGnjsKjlTMdrKyTcLCKeQp/n4AAMFJ961wMbp+09y8WuGPClDEv07RIItdXKIXAA==
+ version "14.0.26"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.26.tgz#22a3b8a46510da8944b67bfc27df02c34a35331c"
+ integrity sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==
-"@types/prismjs@^1.16.0":
+"@types/prismjs@^1.16.1":
version "1.16.1"
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.1.tgz#50b82947207847db6abcbcd14caa89e3b897c259"
integrity sha512-RNgcK3FEc1GpeOkamGDq42EYkb6yZW5OWQwTS56NJIB8WL0QGISQglA7En7NUx9RGP8AC52DOe+squqbAckXlA==
@@ -298,15 +298,15 @@
source-map "^0.6.1"
"@types/webpack-sources@*":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-1.4.0.tgz#e58f1f05f87d39a5c64cf85705bdbdbb94d4d57e"
- integrity sha512-c88dKrpSle9BtTqR6ifdaxu1Lvjsl3C5OsfvuUbUwdXymshv1TkufUAXBajCCUM/f/TmnkZC/Esb03MinzSiXQ==
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-1.4.1.tgz#3bed49013ec7935680d781e83cf4b5ce13ddf917"
+ integrity sha512-B/RJcbpMp1/od7KADJlW/jeXTEau6NYmhWo+hB29cEfRriYK9SRlH8sY4hI9Au7nrP95Z5MumGvIEiEBHMxoWA==
dependencies:
"@types/node" "*"
"@types/source-list-map" "*"
source-map "^0.7.3"
-"@types/webpack@*", "@types/webpack@^4.41.6":
+"@types/webpack@*", "@types/webpack@^4.41.18":
version "4.41.21"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.21.tgz#cc685b332c33f153bb2f5fc1fa3ac8adeb592dee"
integrity sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==
@@ -489,9 +489,9 @@ ajv-errors@^1.0.0:
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.1.tgz#b83ca89c5d42d69031f424cad49aada0236c6957"
- integrity sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2:
version "6.12.3"
@@ -632,11 +632,6 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
-ast-types@0.9.6:
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
- integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=
-
async-each@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
@@ -659,6 +654,11 @@ async@~1.0.0:
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=
+at-least-node@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+ integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
@@ -955,13 +955,13 @@ callsites@^2.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
-camel-case@3.0.x:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
- integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+camel-case@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547"
+ integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==
dependencies:
- no-case "^2.2.0"
- upper-case "^1.1.1"
+ pascal-case "^3.1.1"
+ tslib "^1.10.0"
camelcase@^5.3.1:
version "5.3.1"
@@ -969,9 +969,9 @@ camelcase@^5.3.1:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001097:
- version "1.0.30001105"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001105.tgz#d2cb0b31e5cf2f3ce845033b61c5c01566549abf"
- integrity sha512-JupOe6+dGMr7E20siZHIZQwYqrllxotAhiaej96y6x00b/48rPt42o+SzOSCPbrpsDWvRja40Hwrj0g0q6LZJg==
+ version "1.0.30001107"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a"
+ integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ==
chalk@^1.1.1:
version "1.1.3"
@@ -1042,7 +1042,7 @@ chokidar@^2.1.8:
optionalDependencies:
fsevents "^1.2.7"
-chokidar@^3.2.2, chokidar@^3.4.0:
+chokidar@^3.2.2, chokidar@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1"
integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==
@@ -1092,7 +1092,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-clean-css@4.2.x:
+clean-css@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
@@ -1167,20 +1167,15 @@ colors@^1.1.2:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
-commander@2.17.x:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
- integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-
commander@^2.18.0, commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-commander@~2.19.0:
- version "2.19.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
- integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+commander@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
+ integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commondir@^1.0.1:
version "1.0.1"
@@ -1341,7 +1336,7 @@ css-has-pseudo@^0.10.0:
postcss "^7.0.6"
postcss-selector-parser "^5.0.0-rc.4"
-css-loader@^3.4.2:
+css-loader@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645"
integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==
@@ -1520,7 +1515,7 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
-dom-serializer@0:
+dom-serializer@0, dom-serializer@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
@@ -1558,6 +1553,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
+domhandler@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9"
+ integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==
+ dependencies:
+ domelementtype "^2.0.1"
+
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@@ -1574,6 +1576,23 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
+domutils@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.1.0.tgz#7ade3201af43703fde154952e3a868eb4b635f16"
+ integrity sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg==
+ dependencies:
+ dom-serializer "^0.2.1"
+ domelementtype "^2.0.1"
+ domhandler "^3.0.0"
+
+dot-case@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa"
+ integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==
+ dependencies:
+ no-case "^3.0.3"
+ tslib "^1.10.0"
+
dot-prop@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
@@ -1597,9 +1616,9 @@ duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.6.0:
stream-shift "^1.0.0"
electron-to-chromium@^1.3.488:
- version "1.3.504"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.504.tgz#54d6288202f8453053c006eb862e2e3b7bc867a5"
- integrity sha512-yOXnuPaaLAIZUVuXHYDCo3EeaiEfbFgYWCPH1tBMp+jznCq/zQYKnf6HmkKBmLJ0VES81avl18JZO1lx/XAHOw==
+ version "1.3.510"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.510.tgz#dee781ff8b595c0deb60172b75d50b6889757eda"
+ integrity sha512-sLtGB0znXdmo6lM8hy5wTVo+fLqvIuO8hEpgc0DvPmFZqvBu/WB7AarEwhxVKjf3rVbws/rC8Xf+AlsOb36lJQ==
elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.3"
@@ -1641,7 +1660,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0:
+enhanced-resolve@^4.0.0, enhanced-resolve@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126"
integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==
@@ -1674,14 +1693,6 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
-es6-templates@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz#5cb9ac9fb1ded6eb1239342b81d792bbb4078ee4"
- integrity sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=
- dependencies:
- recast "~0.11.12"
- through "~2.3.6"
-
escalade@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
@@ -1710,11 +1721,6 @@ esprima@^4.0.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-esprima@~3.1.0:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
- integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
-
esrecurse@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
@@ -1804,23 +1810,18 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-fastparse@^1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
- integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
-
figgy-pudding@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
-file-loader@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-5.1.0.tgz#cb56c070efc0e40666424309bd0d9e45ac6f2bb8"
- integrity sha512-u/VkLGskw3Ue59nyOwUwXI/6nuBCo7KBkniB/l7ICwr/7cPNGsL1WCXUp3GB0qgOOKU1TiP49bv4DZF/LJqprg==
+file-loader@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f"
+ integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==
dependencies:
- loader-utils "^1.4.0"
- schema-utils "^2.5.0"
+ loader-utils "^2.0.0"
+ schema-utils "^2.6.5"
file-uri-to-path@1.0.0:
version "1.0.0"
@@ -1875,6 +1876,15 @@ find-cache-dir@^2.1.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
+find-cache-dir@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880"
+ integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^3.0.2"
+ pkg-dir "^4.1.0"
+
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@@ -1882,6 +1892,14 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
+find-up@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
flatten@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b"
@@ -1915,10 +1933,10 @@ from2@^2.1.0:
inherits "^2.0.1"
readable-stream "^2.0.0"
-front-matter@^3.1.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-3.2.1.tgz#88be839638f397bbbcb0d61ac03bd08abb4f0a40"
- integrity sha512-YUhgEhbL6tG+Ok3vTGIoSDKqcr47aSDvyhEqIv8B+YuBJFsPnOiArNXTPp2yO07NL+a0L4+2jXlKlKqyVcsRRA==
+front-matter@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-4.0.2.tgz#b14e54dc745cfd7293484f3210d15ea4edd7f4d5"
+ integrity sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==
dependencies:
js-yaml "^3.13.1"
@@ -1931,6 +1949,16 @@ fs-extra@^8.1.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
+fs-extra@^9.0.1:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
+ integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
+ dependencies:
+ at-least-node "^1.0.0"
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^1.0.0"
+
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -2008,15 +2036,16 @@ gh-pages-deploy@^0.5.1:
prompt "1.0.0"
require-module "^0.1.0"
-gh-pages@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-2.2.0.tgz#74ebeaca8d2b9a11279dcbd4a39ddfff3e6caa24"
- integrity sha512-c+yPkNOPMFGNisYg9r4qvsMIjVYikJv7ImFOhPIVPt0+AcRUamZ7zkGRLHz7FKB0xrlZ+ddSOJsZv9XAFVXLmA==
+gh-pages@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-3.1.0.tgz#ec3ed0f6a6e3fc3d888758fa018f08191c96bd55"
+ integrity sha512-3b1rly9kuf3/dXsT8+ZxP0UhNLOo1CItj+3e31yUVcaph/yDsJ9RzD7JOw5o5zpBTJVQLlJAASNkUfepi9fe2w==
dependencies:
async "^2.6.1"
commander "^2.18.0"
email-addresses "^3.0.1"
filenamify-url "^1.0.0"
+ find-cache-dir "^3.3.1"
fs-extra "^8.1.0"
globby "^6.1.0"
@@ -2169,7 +2198,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-he@1.2.x:
+he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -2183,29 +2212,29 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-html-loader@^0.5.5:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.5.5.tgz#6356dbeb0c49756d8ebd5ca327f16ff06ab5faea"
- integrity sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==
+html-loader@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-1.1.0.tgz#91915f4d274caa9d46d1c3dc847cd82bfc037dbd"
+ integrity sha512-zwLbEgy+i7sgIYTlxI9M7jwkn29IvdsV6f1y7a2aLv/w8l1RigVk0PFijBZLLFsdi2gvL8sf2VJhTjLlfnK8sA==
dependencies:
- es6-templates "^0.2.3"
- fastparse "^1.1.1"
- html-minifier "^3.5.8"
- loader-utils "^1.1.0"
- object-assign "^4.1.1"
+ html-minifier-terser "^5.0.5"
+ htmlparser2 "^4.1.0"
+ loader-utils "^2.0.0"
+ parse-srcset "^1.0.2"
+ schema-utils "^2.6.5"
-html-minifier@^3.5.8:
- version "3.5.21"
- resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
- integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
+html-minifier-terser@^5.0.5:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054"
+ integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==
dependencies:
- camel-case "3.0.x"
- clean-css "4.2.x"
- commander "2.17.x"
- he "1.2.x"
- param-case "2.1.x"
- relateurl "0.2.x"
- uglify-js "3.4.x"
+ camel-case "^4.1.1"
+ clean-css "^4.2.3"
+ commander "^4.1.1"
+ he "^1.2.0"
+ param-case "^3.0.3"
+ relateurl "^0.2.7"
+ terser "^4.6.3"
htmlparser2@^3.9.1:
version "3.10.1"
@@ -2219,6 +2248,16 @@ htmlparser2@^3.9.1:
inherits "^2.0.1"
readable-stream "^3.1.1"
+htmlparser2@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78"
+ integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^3.0.0"
+ domutils "^2.0.0"
+ entities "^2.0.0"
+
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
@@ -2605,6 +2644,15 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
+jsonfile@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
+ integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==
+ dependencies:
+ universalify "^1.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
@@ -2648,7 +2696,7 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
+loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
@@ -2674,6 +2722,13 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -2759,10 +2814,12 @@ lodash@^4.17.14, lodash@^4.17.19:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
-lower-case@^1.1.1:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
- integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+lower-case@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
+ integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==
+ dependencies:
+ tslib "^1.10.0"
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
@@ -2789,7 +2846,7 @@ make-dir@^2.0.0:
pify "^4.0.1"
semver "^5.6.0"
-make-dir@^3.0.0:
+make-dir@^3.0.0, make-dir@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -2813,10 +2870,10 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-marked@^0.8.0:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
- integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==
+marked@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-1.1.1.tgz#e5d61b69842210d5df57b05856e0c91572703e6a"
+ integrity sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==
md5.js@^1.3.4:
version "1.3.5"
@@ -2878,10 +2935,17 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
-mime@^2.4.4:
- version "2.4.6"
- resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
- integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
+mime-db@1.44.0:
+ version "1.44.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
+ integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+
+mime-types@^2.1.26:
+ version "2.1.27"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
+ integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
+ dependencies:
+ mime-db "1.44.0"
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
@@ -3032,12 +3096,13 @@ neo-async@^2.5.0, neo-async@^2.6.1:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
-no-case@^2.2.0:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
- integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+no-case@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8"
+ integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==
dependencies:
- lower-case "^1.1.1"
+ lower-case "^2.0.1"
+ tslib "^1.10.0"
node-libs-browser@^2.2.1:
version "2.2.1"
@@ -3069,11 +3134,11 @@ node-libs-browser@^2.2.1:
vm-browserify "^1.0.1"
node-releases@^1.1.58:
- version "1.1.59"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.59.tgz#4d648330641cec704bff10f8e4fe28e453ab8e8e"
- integrity sha512-H3JrdUczbdiwxN5FuJPyCHnGHIFqQ0wWxo+9j1kAXAzqNMAHlo+4I/sYYxpyK0irQ73HgdiyzD32oqQDcU2Osw==
+ version "1.1.60"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
+ integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
-nodemon@^2.0.2:
+nodemon@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416"
integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==
@@ -3200,7 +3265,7 @@ p-cancelable@^1.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
-p-limit@^2.0.0:
+p-limit@^2.0.0, p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
@@ -3214,6 +3279,13 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@@ -3243,12 +3315,13 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
-param-case@2.1.x:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
- integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+param-case@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238"
+ integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==
dependencies:
- no-case "^2.2.0"
+ dot-case "^3.0.3"
+ tslib "^1.10.0"
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
version "5.1.5"
@@ -3270,6 +3343,19 @@ parse-json@^4.0.0:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
+parse-srcset@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
+ integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
+
+pascal-case@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f"
+ integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==
+ dependencies:
+ no-case "^3.0.3"
+ tslib "^1.10.0"
+
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
@@ -3290,6 +3376,11 @@ path-exists@^3.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -3345,6 +3436,13 @@ pkg-dir@^3.0.0:
dependencies:
find-up "^3.0.0"
+pkg-dir@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
pkginfo@0.3.x:
version "0.3.1"
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21"
@@ -3550,14 +3648,14 @@ postcss-modules-extract-imports@^2.0.0:
postcss "^7.0.5"
postcss-modules-local-by-default@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915"
- integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0"
+ integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==
dependencies:
icss-utils "^4.1.1"
- postcss "^7.0.16"
+ postcss "^7.0.32"
postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.0.0"
+ postcss-value-parser "^4.1.0"
postcss-modules-scope@^2.2.0:
version "2.2.0"
@@ -3704,7 +3802,7 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0:
+postcss-value-parser@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
@@ -3718,7 +3816,7 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
+postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.32"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d"
integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==
@@ -3744,11 +3842,6 @@ prismjs@^1.19.0:
optionalDependencies:
clipboard "^2.0.0"
-private@~0.1.5:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
- integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
-
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -3933,20 +4026,10 @@ readdirp@~3.4.0:
dependencies:
picomatch "^2.2.1"
-recast@~0.11.12:
- version "0.11.23"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
- integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=
- dependencies:
- ast-types "0.9.6"
- esprima "~3.1.0"
- private "~0.1.5"
- source-map "~0.5.0"
-
regenerator-runtime@^0.13.3:
- version "0.13.6"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.6.tgz#d236043c46ffab2968c1ef651803d8acdea8ed65"
- integrity sha512-GmwlGiazQEbOwQWDdbbaP10i15pGtScYWLbMZuu+RKRz0cZ+g8IUONazBnaZqe7j1670IV1HgE4/8iy7CQPf4Q==
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
@@ -3970,7 +4053,7 @@ registry-url@^5.0.0:
dependencies:
rc "^1.2.8"
-relateurl@0.2.x:
+relateurl@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
@@ -4089,7 +4172,7 @@ schema-utils@^1.0.0:
ajv-errors "^1.0.0"
ajv-keywords "^3.1.0"
-schema-utils@^2.5.0, schema-utils@^2.6.6, schema-utils@^2.7.0:
+schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
@@ -4226,7 +4309,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
-source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0:
+source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -4394,7 +4477,7 @@ strip-url-auth@^1.0.0:
resolved "https://registry.yarnpkg.com/strip-url-auth/-/strip-url-auth-1.0.1.tgz#22b0fa3a41385b33be3f331551bbb837fa0cd7ae"
integrity sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=
-style-loader@^1.1.3:
+style-loader@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a"
integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg==
@@ -4460,7 +4543,7 @@ terser-webpack-plugin@^1.4.3:
webpack-sources "^1.4.0"
worker-farm "^1.7.0"
-terser@^4.1.2:
+terser@^4.1.2, terser@^4.6.3:
version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
@@ -4477,7 +4560,7 @@ through2@^2.0.0, through2@^2.0.2, through2@^2.0.3:
readable-stream "~2.3.6"
xtend "~4.0.1"
-through@2, through@~2.3.6:
+through@2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -4563,10 +4646,10 @@ truncate-html@^1.0.3:
"@types/cheerio" "^0.22.8"
cheerio "0.22.0"
-ts-loader@^6.2.1:
- version "6.2.2"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58"
- integrity sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==
+ts-loader@^7.0.5:
+ version "7.0.5"
+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5"
+ integrity sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig==
dependencies:
chalk "^2.3.0"
enhanced-resolve "^4.0.0"
@@ -4574,7 +4657,7 @@ ts-loader@^6.2.1:
micromatch "^4.0.0"
semver "^6.0.0"
-ts-node@^8.6.2:
+ts-node@^8.10.2:
version "8.10.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
@@ -4585,7 +4668,7 @@ ts-node@^8.6.2:
source-map-support "^0.5.17"
yn "3.1.1"
-tslib@^1.9.0:
+tslib@^1.10.0, tslib@^1.9.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
@@ -4612,19 +4695,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typescript@^3.7.5:
+typescript@^3.9.6:
version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
-uglify-js@3.4.x:
- version "3.4.10"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
- integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
- dependencies:
- commander "~2.19.0"
- source-map "~0.6.1"
-
undefsafe@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae"
@@ -4673,6 +4748,11 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+universalify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
+ integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
+
unset-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
@@ -4705,11 +4785,6 @@ update-notifier@^4.0.0:
semver-diff "^3.1.1"
xdg-basedir "^4.0.0"
-upper-case@^1.1.1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
- integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
-
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
@@ -4722,14 +4797,14 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
-url-loader@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-3.0.0.tgz#9f1f11b371acf6e51ed15a50db635e02eec18368"
- integrity sha512-a84JJbIA5xTFTWyjjcPdnsu+41o/SNE8SpXMdUvXs6Q+LuhCD9E2+0VCiuDWqgo3GGXVlFHzArDmBpj9PgWn4A==
+url-loader@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.0.tgz#c7d6b0d6b0fccd51ab3ffc58a78d32b8d89a7be2"
+ integrity sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw==
dependencies:
- loader-utils "^1.2.3"
- mime "^2.4.4"
- schema-utils "^2.5.0"
+ loader-utils "^2.0.0"
+ mime-types "^2.1.26"
+ schema-utils "^2.6.5"
url-parse-lax@^3.0.0:
version "3.0.0"
@@ -4799,15 +4874,15 @@ watchpack-chokidar2@^2.0.0:
dependencies:
chokidar "^2.1.8"
-watchpack@^1.6.1:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa"
- integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==
+watchpack@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
+ integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
dependencies:
graceful-fs "^4.1.2"
neo-async "^2.5.0"
optionalDependencies:
- chokidar "^3.4.0"
+ chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.0"
webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1:
@@ -4818,10 +4893,10 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1:
source-list-map "^2.0.0"
source-map "~0.6.1"
-webpack@^4.41.6:
- version "4.43.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6"
- integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==
+webpack@^4.43.0:
+ version "4.44.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.0.tgz#3b08f88a89470175f036f4a9496b8a0428668802"
+ integrity sha512-wAuJxK123sqAw31SpkPiPW3iKHgFUiKvO7E7UZjtdExcsRe3fgav4mvoMM7vvpjLHVoJ6a0Mtp2fzkoA13e0Zw==
dependencies:
"@webassemblyjs/ast" "1.9.0"
"@webassemblyjs/helper-module-context" "1.9.0"
@@ -4831,7 +4906,7 @@ webpack@^4.41.6:
ajv "^6.10.2"
ajv-keywords "^3.4.1"
chrome-trace-event "^1.0.2"
- enhanced-resolve "^4.1.0"
+ enhanced-resolve "^4.3.0"
eslint-scope "^4.0.3"
json-parse-better-errors "^1.0.2"
loader-runner "^2.4.0"
@@ -4844,7 +4919,7 @@ webpack@^4.41.6:
schema-utils "^1.0.0"
tapable "^1.1.3"
terser-webpack-plugin "^1.4.3"
- watchpack "^1.6.1"
+ watchpack "^1.7.4"
webpack-sources "^1.4.1"
widest-line@^3.1.0:
From 34bd74d12d287f5aa56cad5b304c52f2a19db364 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Fri, 10 Jul 2020 13:38:31 -0400
Subject: [PATCH 02/26] planning out/writing docs stuff
---
website/blog/2020-04-15-introducing-crank.md | 14 +-
website/guides/02-elements.md | 2 +-
website/guides/05-special-tags-and-props.md | 17 +-
website/guides/06-lifecycles.md | 6 +-
website/guides/07-working-with-typescript.md | 6 +-
website/guides/08-api-reference.md | 19 --
website/guides/08-reusable-logic.md | 19 ++
website/guides/09-custom-renderers.md | 142 ++++++++++
...-react.md => 10-differences-from-react.md} | 1 -
website/guides/11-api-reference.md | 244 ++++++++++++++++++
10 files changed, 439 insertions(+), 31 deletions(-)
delete mode 100644 website/guides/08-api-reference.md
create mode 100644 website/guides/08-reusable-logic.md
create mode 100644 website/guides/09-custom-renderers.md
rename website/guides/{999999999-differences-from-react.md => 10-differences-from-react.md} (99%)
create mode 100644 website/guides/11-api-reference.md
diff --git a/website/blog/2020-04-15-introducing-crank.md b/website/blog/2020-04-15-introducing-crank.md
index 9c9fb61e3..f8649928d 100644
--- a/website/blog/2020-04-15-introducing-crank.md
+++ b/website/blog/2020-04-15-introducing-crank.md
@@ -6,15 +6,15 @@ publishDate: 2020-04-15T19:18:41.371Z
After months of development, I’m happy to introduce Crank.js, a new framework for creating JSX-driven components with functions, promises and generators. And I know what you’re thinking: *oh no, not another web framework.* There are already so many of them out there and each carries a non-negligible cost in terms of learning it and building an ecosystem to surround it, so it makes sense that you would reject newcomers if only to avoid the deep sense of exhaustion which has come to be known amongst front-end developers as “JavaScript fatigue.” Therefore, this post is both an introduction to Crank as well as an apology: I’m sorry for creating yet another framework, and I hope that by explaining the circumstances which led me to do so, you will forgive me.
-I will be honest. Before embarking on this project, I never considered myself capable of making a “web framework.” I don’t maintain any popular open-source libraries, and most of the early commits to this project had messages like “why on Earth are you doing this?” Before working on Crank, my framework of choice was React, and I had used it dutifully for almost every project within my control since the `React.createClass` days. And as React evolved, I must admit, I was intrigued and excited with the announcement of each new code-named feature like “Fibers,” “Hooks” and “Suspense.” I’d spend hours attempting to decipher tweets from Sebastian Markbage, one of the principal architects behind React, and I sincerely felt like React would continue to be relevant well into the 2020s.
+I will be honest. Before embarking on this project, I never considered myself capable of making a “web framework.” I don’t maintain any popular open-source libraries, and most of the early commits to this project had messages like “I can’t even believe I’m actually considering making my own web framework.” Before working on Crank, my framework of choice was React, and I had used it dutifully for almost every project within my control since the `React.createClass` days. And as React evolved, I must admit, I was intrigued and excited with the announcement of each new code-named feature like “Fibers,” “Hooks” and “Suspense.” I sincerely felt that React would continue to be relevant well into the 2020s.

-However, over time, I grew increasingly alienated by what I perceived to be the more general direction of React, which was to reframe it as a “UI runtime.” Each new API felt exciting, but I disliked how opaque and error-prone the concrete code written with these APIs seemed. I was unhappy, for instance, with the strangeness and pitfalls of the new Hooks API, and I worried about the constant warnings the React team gave about how code which worked today would break once something called “Concurrent Mode” landed. *I already have a UI runtime*, I began to think, whenever I read the latest on React, *it’s called JavaScript.*
+However, over time, I grew increasingly alienated by what I perceived to be the more general direction of React, which was to reframe it as a “UI runtime.” Each new API felt exciting, but I disliked how opaque and error-prone the concrete code written with these APIs seemed. I was unhappy, for instance, with the strangeness and pitfalls of the new Hooks API, and I worried about the constant warnings the React team gave about how code which worked today would break once something called “Concurrent Mode” landed. *I already have a UI runtime*, I began to grumble whenever I read the latest on React, *it’s called JavaScript.*
-Towards the end, I felt marooned, because on the one hand I didn’t feel comfortable using React anymore, but on the other, I didn’t want to use any of the alternative frameworks either. I agreed with the criticisms which Vue and Svelte advocates lobbed in the direction of React, but I was unwilling to convert to these frameworks because they prominently featured HTML template languages as the main way to use them.
+Towards the end, I felt marooned, because on the one hand I didn’t feel comfortable using React anymore, but on the other, I didn’t want to use any of the alternatives either. I agreed with the criticisms which Vue and Svelte advocates lobbed in the direction of React, but I was unwilling to convert to these frameworks because they prominently featured HTML template languages as the main way to use them.
-I like JSX. I like the small surface area it provides compared to template languages, which provide their own syntax to do basic things like iterating over an array or conditionally rendering something. At the same time, the other frameworks which used JSX like Preact and Inferno seemed to follow React blindly in its heroic evolution from “a view layer” into “a UI runtime.” Rather than thinking critically about each new feature, these libraries seemed eager to mimic them for purposes of compatibility, opting to distinguish themselves instead in terms of library metrics like bundle size (Preact) or runtime performance (Inferno). My problems with React weren’t related to bundle size or runtime performance. It was the API itself that needed fixing. I felt like React, which had up to this point been the standard-bearer of JSX, was no longer up to the task of defending its colors.
+I like JSX. I like the small surface area it provides compared to template languages, which provide their own syntax to do basic things like iterating over an array or conditionally rendering something. Meanwhile, the other frameworks which used JSX like Preact and Inferno seemed to follow React blindly in its heroic evolution from “a view layer” into “a UI runtime.” Rather than thinking critically about each new feature, these libraries seemed eager to mimic them for purposes of compatibility, opting to distinguish themselves instead in terms of library metrics like bundle size (Preact) or runtime performance (Inferno). My problems with React weren’t related to bundle size or runtime performance. It was the API itself that needed fixing. I felt like React, which had up to this point been the standard-bearer of JSX, was no longer up to the task of defending its colors.
## Tired of the suspense
@@ -46,7 +46,7 @@ Correspondingly, a lot of the pain points of React began to make sense. All of t
Freed of this dogmatic assertion, I pondered for a week or so on the kind of JSX-based library you could create if components didn’t have to be sync functions. After all, JavaScript has at present four separate function syntaxes (`function`, `async function`, `function *`, and `async function *`); wouldn’t it be nice if we could use this entire palette to write components? Could there be a use-case for generator functions as well? Again the [React team dismissed generators by definition](https://github.com/facebook/react/issues/7942#issuecomment-254987818), because generator functions returned generator objects, which are stateful and therefore “impure.”
## JavaScript is already a UI runtime
-At this point, I was intrigued by this idea but I also didn’t want to write a React alternative. I wanted to write applications, not build and maintain a framework. And so I was about to move on to something else, when my previous work with async iterators and generators gave me a flash of insight. The entire React lifecycle, all the “componentDidWhat” methods, everything which React was trying to do with classes and hooks and state and refs, all of it could be expressed within a single async generator function.
+At this point, I was intrigued by this idea but I also didn’t want to write a React alternative. I wanted to write applications, not build and maintain a framework. And so I was about to move on to something else, when my previous work with async iterators and generators gave me a flash of insight. The entire React lifecycle, all of the `componentDidWhat` methods, everything which React was trying to do with classes and hooks and state and refs, all of it could be expressed within a single async generator function.
```js
async function *MyComponent(props) {
@@ -71,7 +71,7 @@ async function *MyComponent(props) {
}
```
-This is some pseudo-code I sketched out, where the calls to `componentDidWhat` functions are just there to demonstrate where code goes compared to the React lifecycle. The actual Crank API turned out to be different, but in the moment I felt like I had captured lightning in a bottle. By yielding JSX elements rather than returning them, you could have code which ran before or after the component rendered, emulating the `componentWillUpdate` or `componentDidUpdate` lifecycle methods. New props could be passed in by stepping through a framework-provided async iterator, which resumed with fresh props whenever the component was rerendered. And the concept of local state, which in React requires calls to `this.setState` or the `useState` hook, could simply be expressed with local variables, because yielding is not final and the generator’s local scope could be preserved between renders.
+This is some pseudo-code I sketched out, where the calls to `componentDidWhat` functions merely demonstrate where code goes compared to the React lifecycle. While the actual Crank API turned out to be slightly different, in the moment I felt like I had captured lightning in a bottle. By yielding JSX elements rather than returning them, you could have code which ran before or after the component rendered, emulating the `componentWillUpdate` or `componentDidUpdate` lifecycle methods. New props could be passed in by stepping through a framework-provided async iterator, which resumed with fresh props whenever the component was rerendered. And the concept of local state, which in React requires calls to `this.setState` or the `useState` hook, could simply be expressed with local variables, because yielding is not final and the generator’s local scope could be preserved between renders.
Furthermore, you could implement something like the `componentDidCatch` and `componentWillUnmount` lifecycle methods directly within the async generator by wrapping the `yield` operator in a `try`/`catch`/`finally` block. And the framework could, upon producing DOM nodes, pass these nodes back into the generator, so you could do direct DOM manipulations without React’s notion of “refs.” All these things which React required separate methods or hooks to accomplish could be done within async generator functions with just the control-flow operators that JavaScript provides, and all within the same scope.
@@ -86,4 +86,4 @@ By combining these relatively old, almost boring technologies with JSX syntax, I
And again, I sincerely apologize for creating yet another framework in an already crowded space, but I hope, if you’ve read this far, you understand why I did so, namely, because I thought React was dropping the ball in terms of its newest APIs, because I still wanted to use JSX, and because of the sudden realization that we could be doing so much more with the different function syntaxes available to us in JavaScript.
-If any of this interests you, if you want to continue to use JSX over template languages, if you’re tired of debugging hooks, if you want to use promises in your components *today*, if you’re looking for a framework which has, arguably, the most “just JavaScript” story for reactivity, I encourage you to check out Crank. You can read [the documentation](/guides/getting-started) or check out the [TodoMVC example](https://codesandbox.io/s/crank-todomvc-k6s0x) that made me tear up a little. Crank is still in its early days, and there’s a lot of work to be done before it can be considered a full-fledged framework, but I think the ideas behind it are sound and I’ve thoroughly enjoyed designing it. I can’t wait to see what people build with Crank.
+If any of this interests you, if you want to continue to use JSX over template languages, if you’re tired of debugging hooks, if you want to use promises in your components *today*, if you’re looking for a framework which has, arguably, the most “just JavaScript” story for reactivity, I encourage you to check out Crank. You can read [the documentation](/guides/getting-started) or check out the [TodoMVC example](https://codesandbox.io/s/crank-todomvc-k6s0x) that made me cry a little haha. Crank is still in its early days, and there’s a lot of work to be done before it can be considered a full-fledged framework, but I think the ideas behind it are sound and I’ve thoroughly enjoyed designing it. I can’t wait to see what people build with Crank.
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index 485a00d0d..6791772cb 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -110,7 +110,7 @@ console.log(document.body.innerHTML); // "
123 abc
"
```
## Element diffing
-Crank uses the same “virtual DOM diffing” algorithm made popular by React, where elements of the tree are compared by tag and position between renders, and subtrees whose root tags don’t match are thrown away. This allows you to write declarative code which focuses on producing the right element tree, while Crank does the dirty work of managing state and mutating the DOM.
+Crank uses the same “virtual DOM diffing” algorithm made popular by React, where DOM operations and component state is determined by the element’s tag and position in the tree. This approach allows you to write declarative code which focuses on producing the right elements, while Crank does the dirty work of managing stateful components and mutating the DOM.
```jsx
renderer.render(
diff --git a/website/guides/05-special-tags-and-props.md b/website/guides/05-special-tags-and-props.md
index b5dd6fdc1..96f356d31 100644
--- a/website/guides/05-special-tags-and-props.md
+++ b/website/guides/05-special-tags-and-props.md
@@ -4,7 +4,9 @@ title: Special Props and Tags
The element diffing algorithm used by Crank is both declarative and efficient, but there are times when you might want to tweak the way it works. Crank provides special props and tags which produce different rendering behaviors.
-## Special Props
+## Special Crank Props
+### children
+
### crank-key
By default, Crank will use an element’s tag and position to determine if it represents an update or a change to the tree. Because elements often represent stateful DOM nodes or components, it can be useful to *key* the children of an element to hint to renderers that an element has been added, moved or removed. In Crank, we do this with the special prop `crank-key`:
@@ -71,6 +73,19 @@ console.log(document.body.innerHTML);
console.log(document.firstChild.firstChild === span); // true
```
+### crank-ref
+TKTKTKTKTKTKTKTKTKTKTKTK
+
+## Special DOM Props
+### style
+
+### innerHTML
+
+### onevent
+
+### class/className
+
+
## Special Tags
Crank provides several element tags which have special meaning when rendering. In actuality, these tags are symbols and behave similarly to string tags, except they affect the diffing algorithm and output.
diff --git a/website/guides/06-lifecycles.md b/website/guides/06-lifecycles.md
index 52c2021e8..975912b8a 100644
--- a/website/guides/06-lifecycles.md
+++ b/website/guides/06-lifecycles.md
@@ -2,7 +2,11 @@
title: Lifecycles
---
-Crank uses the full power and expressiveness of generator functions to encapsulate the notion of lifecycles within the same variable scope. Internally, Crank achieves this by calling the `next`, `return` and `throw` methods of the generator object as components are inserted, updated and removed from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
+Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the generator object as components are inserted, updated and removed from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
+
+## A review of generator functions
+
+TODO
## Returning from a generator
diff --git a/website/guides/07-working-with-typescript.md b/website/guides/07-working-with-typescript.md
index 15d6e79b7..77dd43e6f 100644
--- a/website/guides/07-working-with-typescript.md
+++ b/website/guides/07-working-with-typescript.md
@@ -96,7 +96,7 @@ function Greeting ({name, children}: {name: string, children: Children}) {
```
## Typing event listeners
-If you dispatch custom events, you’re going to want parent event listeners to be typed with the event you bubbled automatically. To do so, you can extend a global `EventMap` type provided by Crank.
+If you dispatch custom events, you may want parent event listeners to be typed with the event you bubbled automatically. To do so, you can use module augmentation to extend the `EventMap` interface provided by Crank.
```tsx
declare global {
@@ -121,3 +121,7 @@ function MyButton (props) {
);
}
```
+
+## Typing provisions
+By default, calls to `Context.prototype.get` and `Context.prototype.set` will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.
+
diff --git a/website/guides/08-api-reference.md b/website/guides/08-api-reference.md
deleted file mode 100644
index a0da10166..000000000
--- a/website/guides/08-api-reference.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: API Reference
-publish: false
----
-## API Reference
-TODO
-
-## `@bikeshaving/crank`
-### `createElement`
-### `Element`
-### `Child`
-### `Context`
-### `Renderer`
-## `@bikeshaving/crank/dom`
-### `DOMRenderer`
-### `renderer`
-## `@bikeshaving/crank/html`
-### `HTMLRenderer`
-### `renderer`
diff --git a/website/guides/08-reusable-logic.md b/website/guides/08-reusable-logic.md
new file mode 100644
index 000000000..4d1b9985b
--- /dev/null
+++ b/website/guides/08-reusable-logic.md
@@ -0,0 +1,19 @@
+---
+title: Reusable Logic
+---
+
+## Additional Context methods
+
+### Provisions
+
+### Scheduling and cleanup
+
+## Strategies for writing Crank libraries
+
+### Global Context extensions
+
+### Context helper factories
+
+### Higher-order components
+
+### Async iterators
diff --git a/website/guides/09-custom-renderers.md b/website/guides/09-custom-renderers.md
new file mode 100644
index 000000000..561e7b546
--- /dev/null
+++ b/website/guides/09-custom-renderers.md
@@ -0,0 +1,142 @@
+---
+title: Custom Renderers
+---
+
+The core Crank module provides an abstract `Renderer` class which can be extended to produce more than just DOM nodes or HTML strings, allowing you to target alternative environments such as WebGL-based canvas libraries, terminals, smartphones or smart TVs. This guide provides an overview of the concepts and internal methods which you will need to know when implementing a custom renderer yourself. Alternatively, you can read through the [DOM](https://github.com/bikeshaving/crank/blob/master/src/dom.ts?ts=2) and [HTML](https://github.com/bikeshaving/crank/blob/master/src/html.ts?ts=2) renderer implementations to learn by example.
+
+**Warning:** The custom renderer API is currently unstable both because of its performance-sensitive nature and because the exact complications of rendering to a wide variety of environments are not yet fully known. If you maintain a Crank renderer, you *will* have to deal with breaking changes as Crank is optimized and as new renderer requirements are discovered.
+
+## The lifecycle of an element
+
+Crank does not provide lifecycle methods or hooks as part of its public interface, instead opting to rely on the natural lifecycle of generator functions. However, we use common lifecycle terms like *mounting*, *updating*, *unmounting* and *committing* internally to conceptualize the process of rendering.
+
+Rendering is essentially a depth-first walk of an element tree, where we recursively compare new elements to what was previously rendered. When elements are new to the tree, we “mount” the element, when elements have been seen before, we “update” the element, and when elements no longer exist in the tree, they are “unmounted.”
+
+“Committing” is the part of rendering process where we actually perform the operations which create, mutate and dispose of nodes. Elements are committed in a *post-order traversal* of the tree, meaning that by the time a specific element is committed, all of its children will have already committed as well. This is done so that rendering side-effects happen all at once, even if there are async components in the tree, which leads to a more consistent and performant user experience. By contrast, components can be thought of as executing in a *pre-order traversal* of the tree, because the only way to get the children of a component element is to execute its component.
+
+## Types associated with the Renderer class
+
+**Note:** Renderer development is considerably more abstract than application development, and using the TypeScript types provided by Crank can make this process much easier to understand. Therefore, this guide both assumes familiarity with TypeScript and uses TypeScript syntax in its examples.
+
+The renderer class takes four type parameters which describe the types of values as they flow in and out of the custom renderer methods.
+
+```ts
+class Renderer<
+ TNode extends object,
+ TScope = unknown,
+ TRoot extends object = TNode,
+ TResult = ElementValue
+>
+```
+
+- `TNode` is the most important type: it is the type of the node associated with each host element. For instance, for the basic DOM renderer, TNode is the [DOM Node interface](https://developer.mozilla.org/en-US/docs/Web/API/Node).
+- `TScope` is the type of the *scope*, a renderer-specific concept for arbitrary data which can passed down the tree between host elements. Scopes are useful for passing contextual information down the tree to be used when nodes are created; for instance, the DOM renderer uses the scope to pass down information about whether we’re currently rendering in an SVG element.
+- `TRoot` is the type of the root node. This is the type of the second parameter passed to the `Renderer.render` method, and the `root` prop passed to `Portal` elements. It is usually the same type as `TNode` but can vary according to renderer requirements.
+- `TResult` describes the type of values made visible to renderer consumers. Any time Crank exposes an internal node, for instance, via the `crank-ref` callback, or as the result of yield expressions in generator components, the renderer can intercept this access and provide something other than the internal nodes, allowing renderers to hide implementation details and provide results which make more sense for a specific environment.
+ For example, the HTML string renderer has an internal node representation, but converts these nodes to strings before they’re exposed to consumers. This is done because the internal nodes must be referentially unique and mutated during rendering, while JavaScript strings are referentially transparent and immutable. Therefore, the `TResult` of the `HTMLRenderer` subclass is `string`.
+
+## Methods
+The following is a description of the signatures of internal renderer methods and when they’re executed.
+
+### Renderer.prototype.create
+
+```ts
+create(
+ tag: string | symbol, props: Record, scope: TScope
+): TNode;
+```
+
+The `create` method is called for each host element the first time the element is committed. The tag and props parameters are the tag and props of the host element which initiated this call, and the scope is the current scope of the element. The return value is the node which will be associated with the host element.
+
+By default, this method will throw a `Not Implemented` error, so custom renderers should always implement this method.
+
+By default, escape returns the same string that was passed in.
+
+### Renderer.prototype.patch
+```ts
+patch(
+ tag: string | symbol, props: Record, node: TNode, scope: TScope,
+): unknown;
+```
+
+The `patch` method is called for each host element whenever it is committed. The tag and props are the tag and props of the associated host element, the node is the value produced by the `create` method when the value was mounted, and the scope is the current scope of the element.
+
+This method is useful for mutating nodes whenever the host element is committedk. It is optional and its return value is ignored.
+
+### Renderer.prototype.arrange
+```ts
+arrange(
+ tag: string | symbol, props: Record, parent: TNode | TRoot, children: Array
+): unknown;
+```
+
+The `arrange` method is called for each host element whenever it is committed. The tag and props are the tag and props of the associated host element, the parent is the value created by the create method for a host node. The `arrange` is also called for every root/portal element, so parent can be the second parameter passed to `Renderer.render`, or the `root` prop passed to `Portal` elements. The `children` of `arrange` is always an array of nodes and strings, which are determined by the related parent element’s children.
+
+In addition to being called when a host or portal element is committed, the `arrange` method can also be called as the last step of a component `refresh`. Because the component’s children may have changed, the nearest ancestor host or portal element has to be rearranged so that the parent node can handle the new children.
+
+This method is where the magic happens, and is useful for connecting the nodes of your target environment in tree form. If your target environment has a separate It is optional and the return value is ignored.
+
+### Renderer.prototype.scope
+```ts
+scope(
+ tag: string | symbol, props: Record, scope: TScope | undefined
+): TScope;
+```
+
+The `scope` method is called for each host or portal element as elements are mounted or updated. Unlike the other custom renderer methods, the `scope` method is called during the pre-order traversal of the tree, much like components are. The `scope` method is passed the tag and props of the relevant host element, as well as the current scope, and the return value becomes the scope which is passed to the `create` and `scope` methods which are called for child elements.
+
+### Renderer.prototype.escape
+```ts
+escape(text: string, scope: TScope): string;
+```
+
+The `escape` method is called whenever a string is encountered in the element tree. It is mainly useful when creating string-based renderers like HTML or XML string renderers, because most rendering targets like the DOM provide text node interfaces which sanitize inputs by default. One important detail is that `escape` should not return text nodes or anything besides a string. We defer this step to the `arrange` method because this allows us to normalize a host element’s children by concatenating adjacent strings before it is passed to `arrange`.
+
+By default, the `escape` method returns the string which was passed in.
+
+### Renderer.prototype.parse
+```ts
+parse(text: string, scope: TScope): TNode | string;
+```
+
+When a `Raw` element is committed, if its `value` prop is a string, we call the `parse` method with that string and the current scope. The return value is the parsed node, or it can be a string as well, in which case parse will be handled like a string child by parents. The `escape` method will not be called on the return value.
+
+By default, the `parse` method returns the string which was passed in.
+
+### Renderer.prototype.dispose
+```ts
+dispose(
+ tag: string | symbol, props: Record, node: TNode
+): unknown
+```
+
+When a host element is unmounted, we call the `dispose` method with the related host element’s tag, props and node. This method is useful if you need to manually release a node or remove event listeners from it for garbage collection purposes.
+
+This method is optional and its return value is ignored.
+
+### Renderer.prototype.complete
+```ts
+complete(root: TRoot): unknown;
+```
+
+The `complete` method is called at the end of every render execution, when all elements have been committed and all other renderer methods have been called. It is useful, if your rendering target needs to be manually rerendered before any node mutations or rearrangements take effect.
+
+This method is optional and its return value is ignored.
+
+### Renderer.prototype.read
+```ts
+read(value: Array | TNode | string | undefined): TResult;
+```
+
+The renderer will expose rendered values in the following places:
+
+- As the return value of `Renderer.prototype.render`
+- As the return value of `Context.prototype.refresh`
+- As the argument passed to `crank-ref` props
+- As the argument passed to `Context.prototype.schedule` and `Context.prototype.cleanup`
+- Via the `Context.prototype.value` getter
+- As the yield value of generator components
+
+When an element or elements are read in this way, we call the `read` method to give renderers a chance to manipulate what is exposed so as to hide internal implementation details and return something which makes sense for the target environment. The parameter passed to read can be a node, a string, undefined, or an array of nodes and strings. The return value is what is actually exposed.
+
+This method is optional. By default, read is an identity function which returns the value passed in.
diff --git a/website/guides/999999999-differences-from-react.md b/website/guides/10-differences-from-react.md
similarity index 99%
rename from website/guides/999999999-differences-from-react.md
rename to website/guides/10-differences-from-react.md
index 60fd8d7e2..f454b266c 100644
--- a/website/guides/999999999-differences-from-react.md
+++ b/website/guides/10-differences-from-react.md
@@ -1,6 +1,5 @@
---
title: Differences from React
-publish: false
---
Though Crank is very much inspired by and similar to React, exact compatibility is a non-goal, and we’ve used this as opportunity to “fix” a lot of pain points with React which bothered us over the years. The following is a list of differences with React, as well as justifications for why we chose to implement features differently.
diff --git a/website/guides/11-api-reference.md b/website/guides/11-api-reference.md
new file mode 100644
index 000000000..e80ca36a8
--- /dev/null
+++ b/website/guides/11-api-reference.md
@@ -0,0 +1,244 @@
+---
+title: API Reference
+---
+
+## Types
+### `Tag`
+A type which represents all valid values which can be used for the tag of an element.
+
+**Example:**
+
+```ts
+let tag: Tag;
+
+// VALID ASSIGNMENTS
+tag = "div";
+tag = Symbol("div");
+function MyComponent() {
+}
+tag = MyComponent;
+
+// INVALID ASSIGNMENTS
+// @ts-expect-error
+tag = 1;
+// @ts-expect-error
+tag = {};
+```
+
+### `TagProps`
+A helper type to map the tag of an element to its expected props.
+
+**Type Parameters:**
+- `TTag` - The tag of the associated element.
+
+**Example:**
+```ts
+function Greeting({name}: {name: string}) {
+ return
];
+// @ts-expect-error
+child = {};
+// @ts-expect-error
+child = new Promise(() => {});
+// @ts-expect-error
+child = new RegExp("Hello");
+// @ts-expect-error
+child = Symbol("Hello");
+```
+
+### `Children`
+A type which describes all valid values of an element tree, including arbitrarily nested iterables of such values.
+
+**Example:**
+```ts
+let children: Children;
+
+// VALID ASSIGNMENTS
+children = "hello";
+children = 1;
+children = true;
+children = false;
+children = null;
+children = undefined;
+children =
Hello
;
+children = [
Hello
,
World
];
+children = new Set([
Hello
,
World
]);
+
+// INVALID ASSIGNMENTS
+// @ts-expect-error
+children = {};
+// @ts-expect-error
+children = new RegExp("Hello");
+// @ts-expect-error
+children = Symbol("Hello");
+```
+
+### `Component`
+A type which represents all functions which can be used as a component.
+
+**Type Parameters:**
+- `TProps` - The expected props of the component
+
+### `ElementValue`
+A helper type which repesents all the possible rendered values of an element.
+
+**Type Parameters:**
+- `TNode` - The node type for the element as created by the renderer.
+
+### `EventMap`
+An interface which maps Event type strings to event subclasses. Can be extended via TypeScript module augmentation for strongly typed event listeners.
+
+**Example:**
+```ts
+declare global {
+ module "@bikeshaving/crank" {
+ interface EventMap {
+ click: MouseEvent;
+ }
+ }
+}
+```
+
+### `ProvisionMap`
+An interface which can be extended to provide strongly typed provisions. See `Context.prototype.get` and `Context.prototype.set`.
+
+**Example:**
+```ts
+declare global {
+ module "@bikeshaving/crank" {
+ interface ProvisionMap {
+ greeting: string;
+ }
+ }
+}
+```
+
+## Special Tags
+### `Fragment`
+A special element tag for grouping multiple children within a parent.
+
+### `Portal`
+A special element tag for creating a new element subtree with a different root, passed via the root prop.
+
+**Props:**
+- root - TODO
+
+### `Copy`
+A special element tag which copies whatever child appeared previously in the element’s position.
+
+### `Raw`
+A special element tag for injecting raw nodes into an element tree via its value prop.
+
+**Props:**
+- value - TODO
+
+## Functions
+### `createElement`
+**Parameters:**
+- `tag: string | symbol | Component` - TODO
+- `props: Record` - TODO
+- `...children: Children` - TODO
+
+Creates an element with the specified tag, props and children. This function is typically used as a target for JSX transpilers, rather than called directly.
+
+### `isElement`
+**Parameters:**
+- `value: unknown` - The value to be tested.
+
+Returns true if the value passed in is a Crank Element.
+
+### `cloneElement`
+**Parameters:**
+- `element: Element` - The Element to be cloned.
+
+Clones the passed in element.
+
+**Remarks:**
+Throws a `TypeError` if the value passed in is not an element.
+
+## Classes
+### `Element`
+Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by renderers to produce and manage stateful nodes.
+
+#### Properties
+- `tag`
+- `props`
+- `key`
+- `ref`
+
+#### Methods
+- [`constructor`](#Element.constructor)
+
+### `Renderer`
+An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
+
+**Type Parameters:**
+- `TNode`
+- `TScope`
+- `TRoot`
+- `TResult`
+
+#### Methods
+- [`constructor`](#Renderer.constructor)
+ Creates a new Renderer. The base `Renderer` constructor accepts no parameters.
+
+- [`render`](#Renderer.render)
+ **Parameters:**
+ - `children: Children`
+ - `root?: TRoot`
+ - `ctx?: Context`
+
+**NOTE:** The internal Crank renderer methods documented in the [guide on custom renderers.](./custom-renderers)
+
+### `Context`
+A class which is instantiated and passed to every component function as its `this` value.
+
+**Type Parameters:**
+- `TProps` - The expected props of the related component.
+- `TResult` - The expected result from rendering the component.
+
+#### Properties
+- `props` - **Readonly** The current props of the component.
+- `value` - **Readonly** The current rendered value of the component.
+
+#### Methods
+- [`[Symbol.iterator]`](#Context[Symbol.iterator])
+- [`[Symbol.asyncIterator]`](#Context[Symbol.asyncIterator])
+- [`get`](#Context.get)
+- [`set`](#Context.set)
+- [`refresh`](#Context.refresh)
+- [`schedule`](#Context.schedule)
+- [`cleanup`](#Context.cleanup)
+- [`addEventListener`](#Context.addEventListener)
+- [`removeEventListener`](#Context.removeEventListener)
+- [`dispatchEvent`](#Context.dispatchEvent)
From bfd909c1a66fff1496413467ed2370c42ffd7143 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Sat, 11 Jul 2020 21:52:19 -0400
Subject: [PATCH 03/26] drafting some stuff
---
README.md | 20 +-
website/build.tsx | 2 +-
website/guides/07-reusable-logic.md | 256 ++++++++++++++++++
website/guides/08-reusable-logic.md | 19 --
...cript.md => 08-working-with-typescript.md} | 0
website/webpack.tsx | 2 +-
6 files changed, 272 insertions(+), 27 deletions(-)
create mode 100644 website/guides/07-reusable-logic.md
delete mode 100644 website/guides/08-reusable-logic.md
rename website/guides/{07-working-with-typescript.md => 08-working-with-typescript.md} (100%)
diff --git a/README.md b/README.md
index 1b4be75d6..041718b7f 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,23 @@ Write JSX-driven components with functions, promises and generators.
Documentation is available at [crank.js.org](https://crank.js.org). Crank.js is in a beta phase, and some APIs may change. To read more about the motivations for this library, you can read the [introductory blog post](https://crank.js.org/blog/introducing-crank).
## Features
-### Declarative components
-Crank uses the same JSX syntax and diffing algorithm popularized by React, allowing you to write HTML-like code directly in your JavaScript.
+### Declarative
+Crank uses the same JSX syntax and diffing algorithm popularized by React, allowing you to write HTML-like syntax directly in your JavaScript.
-
-### Just JavaScript™
+### Just Functions
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.
-### Promises today
-Crank provides first-class support for promises. You can use async/await directly in components, and race components to display fallback UIs.
+### Promise-fluent
+Crank provides first-class support for promises. You can use async/await directly in components, and race async components to display fallback UIs.
+
+### Lightweight
+Crank has no dependencies, and its core is a single file. It currently measures at [4.5KB minified and gzipped](https://bundlephobia.com/result?p=@bikeshaving/crank).
+
+### Performant
+[According to synthetic benchmarks](https://krausest.github.io/js-framework-benchmark/current.html), Crank beats React in terms of execution time and memory usage. It‘s current performance is comparable to Preact and Vue.
+
+### Extensible
+TKTKTK WRITE ABOUT LIBRARY PATTERNS AND CUSTOM RENDERERS.
## Installation
Crank is available on [NPM](https://npmjs.org/@bikeshaving/crank) in the ESModule and CommonJS formats.
diff --git a/website/build.tsx b/website/build.tsx
index 378ef5e8e..b2a01baaa 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -205,7 +205,7 @@ function Home(): Element {
-
Just JavaScript™
+
Just Functions
All components in Crank are just functions or generator functions.
No classes, hooks, proxies or template languages are needed.
diff --git a/website/guides/07-reusable-logic.md b/website/guides/07-reusable-logic.md
new file mode 100644
index 000000000..a64a42b7c
--- /dev/null
+++ b/website/guides/07-reusable-logic.md
@@ -0,0 +1,256 @@
+---
+title: Reusable Logic
+---
+
+## Additional Context properties and methods
+Crank provides additional utilities via Contexts to help you write utilities and share logic between components via plugins and extensions. Most of the APIs here are mainly for library authors and should not be used in the course of typical component development.
+
+### Provisions
+Crank allows you to provide data to all of a component’s descendants via the methods `Context.prototype.get` and `Context.prototype.set`. The `set` method sets a “provision” under a specific key, and the `get` method.
+
+```ts
+function GreetingProvider({greeting, children}) {
+ this.set("greeting", greeting);
+ return children;
+}
+
+function Greeting({name}) {
+ const greeting = this.get("greeting");
+ return
"
+```
+
+Provisions are useful for when you want to coordinate components based on their relationships within the element tree. Provisions allow libraries to define components which interact with their descendants without rigidly defined component hierarchies or requiring the library user to pass data manually between components as props. Anything can be a key for the `get` and `set` methods, so you can use a symbol to ensure that the provision you pass between components are private and do not collide with contexts set by others. Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh components when `set` is called, so it’s up to you to make sure consumers update when providers update.
+
+### Callback-based methods
+Contexts provides two utility methods which take callbacks:
+
+#### `Context.prototype.schedule`
+You can pass a callback to the `schedule` method to listen for when the component commits. This can be deferred if the component runs asynchronously or has async children.
+
+```ts
+EXAMPLE TKKTKTKTK
+```
+
+Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only parameter. They only fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
+
+#### `Context.prototype.cleanup`
+Similarly, you can pass a callback to the `cleanup` method to listen for when the component unmounts.
+
+```ts
+EXAMPLE TKTKTKTKTK
+```
+
+All `cleanup` callbacks fire synchronously when the component is removed, and only once per registered callback function.
+
+### Helpful context getters
+
+#### `Context.prototype.props`
+The current props of a component can be accessed as via the readonly context property `props`. We recommended that you access props within components via component parameters or context iterators, but the `props` property can be useful when you need to access a component’s current props from within a plugin or extension.
+
+#### `Context.prototype.value`
+Similarly, the most recently rendered value of a component is accessible via the readonly context property `value`. Again, we recommended that you access rendered values via the many methods described in [accessing rendered values](#KTKTKTKTK), but it can be useful to access the current value directly when writing helper context methods.
+
+## Strategies for writing Crank libraries
+
+The following are various patterns you can use to write and reuse logic between components, as well as a description of their tradeoffs. We will be wrapping [`window.setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) in examples to demonstrate each design pattern.
+
+### Global Context Extensions
+You can import and extend the Context class’s prototype to globally extend all contexts in your application.
+
+```ts
+import {Context} from "@bikeshaving/crank";
+
+const ContextIntervalSymbol = Symbol.for("ContextIntervalSymbol");
+
+Context.prototype.setInterval = function(callback, delay, ...args) {
+ const interval = window.setInterval(callback, delay, ...args);
+ if (typeof this[ContextIntervalSymbol] === "undefined") {
+ this[ContextIntervalSymbol] = new Set();
+ this.cleanup(() => {
+ for (const interval of this[ContextIntervalSymbol]) {
+ window.clearInterval(interval);
+ }
+ });
+ }
+
+ this[ContextIntervalSymbol].add(interval);
+};
+
+Context.prototype.clearInterval = function(interval) {
+ if (typeof this[ContextIntervalSymbol] !== "undefined") {
+ this[ContextIntervalSymbol].delete(interval);
+ }
+
+ window.clearInterval(interval);
+}
+
+function *Counter() {
+ let seconds = 0;
+ this.setInterval(() => {
+ seconds++;
+ this.refresh();
+ }, 1000);
+
+ while (true) {
+ yield
Seconds: {seconds}
;
+ }
+}
+```
+
+In this example, we define the methods `setInterval` and `clearInterval` directly the `Context` prototype. The example also demonstrates caching intervals on a set which is hidden using an unexported symbol. You can use symbols to hide the internal state of your global context extensions from users.
+
+**Pros:**
+- Methods are available to every component automatically.
+
+**Cons:**
+- Globally scoped.
+- No way to write setup logic.
+- No way to respond to props updates.
+
+**Use cases:**
+Global context extensions are useful for creating Crank-specific wrappers around already global, well-known APIs like `setInterval`, `requestAnimationFrame` or `fetch`.
+
+### Context helper factories
+As an alternative to global context extensions, you can write factory functions which are passed contexts to scope your logic per component.
+
+```ts
+function createSetInterval(ctx, callback, delay, ...args) {
+ const interval = window.setInterval(callback, delay, ...args);
+ ctx.cleanup(() => window.clearInterval(interval));
+ return interval;
+}
+
+function *Counter() {
+ let seconds = 0;
+ const setInterval = createSetInterval(this);
+ setInterval(() => {
+ seconds++;
+ this.refresh();
+ }, 1000);
+
+ while (true) {
+ yield
Seconds: {seconds}
;
+ }
+
+}
+```
+
+Instead of defining the `setInterval` method globally, we define it locally by passing the context of the component into the `createSetInterval` function.
+
+**Pros:**
+- Locally scoped.
+- Explicitly imported and referenced.
+- Setup logic can be written directly in the factory function.
+
+**Cons:**
+- Naming factory functions can be difficult.
+- No way to respond to props updates.
+
+**Use cases:**
+Context helper factories are useful when you want to write locally-scoped factories, especially if they require setup logic. Good use-cases include context-aware state management utilities or wrappers around stateful APIs like [mutation observers](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or [HTML drag and drop](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).
+
+### Higher-order components
+Because Crank components are just functions, you can write functions which both take components as parameters and return wrapped component functions.
+
+```ts
+function interval(Component) {
+ return function *WrappedComponent() {
+ let seconds = 0;
+ const interval = window.setInterval(() => {
+ seconds++;
+ this.refresh();
+ }, 1000)
+ try {
+ for (const props of this) {
+ yield ;
+ }
+ } finally {
+ window.clearInterval(interval);
+ }
+ };
+}
+
+const Counter = interval((props) =>
Seconds: {props.seconds}
);
+```
+
+The interval function takes a component function and returns a component which passes the number of seconds as a prop, as well as refreshing the component whenever the interval is fired.
+
+**Pros:**
+- Locally scoped.
+- Explicitly imported and referenced.
+- Able to respond to new props within the returned component.
+
+**Cons:**
+- Naming higher-order functions can be difficult.
+- JavaScript doesn’t provide an easy syntax for decorating functions.
+- Props that the higher-order component pass in may clash with the component’s own expected props.
+
+**Use cases:**
+The main advantage of higher-order components is that you can respond to props in your utilities just like you would with a component. Higher-order components are most useful when you need reusable logic which refreshes a component, like animation utilities, or modify only well-known props, like styled component libraries.
+
+### Async iterators
+Because components can be written as async generator functions, you can integrate utility functions which return async iterators seamlessly with Crank.
+
+```ts
+async function *createInterval(delay) {
+ let available = true;
+ let resolve;
+ const interval = window.setInterval(() => {
+ if (resolve) {
+ resolve(Date.now());
+ resolve = undefined;
+ } else {
+ available = true;
+ }
+ }, delay);
+
+ try {
+ while (true) {
+ if (available) {
+ available = false;
+ yield Date.now();
+ } else {
+ yield new Promise((resolve1) => (resolve = resolve1));
+ }
+ }
+ } finally {
+ window.clearInterval(interval);
+ }
+}
+
+async function *Counter() {
+ let seconds = 0;
+ for await (const _ of createInterval(1000)) {
+ yield
Seconds: {seconds}
;
+ seconds++;
+ }
+}
+```
+
+**Pros:**
+- The utilities you write are framework-agnostic.
+- Uniform logic to dispose of resources.
+
+**Cons:**
+- Promises and async iterators can be prone to race conditions and deadlocks.
+
+**Use cases:**
+If you use async iterators/generators already, Crank is the perfect fit for your application.
diff --git a/website/guides/08-reusable-logic.md b/website/guides/08-reusable-logic.md
deleted file mode 100644
index 4d1b9985b..000000000
--- a/website/guides/08-reusable-logic.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: Reusable Logic
----
-
-## Additional Context methods
-
-### Provisions
-
-### Scheduling and cleanup
-
-## Strategies for writing Crank libraries
-
-### Global Context extensions
-
-### Context helper factories
-
-### Higher-order components
-
-### Async iterators
diff --git a/website/guides/07-working-with-typescript.md b/website/guides/08-working-with-typescript.md
similarity index 100%
rename from website/guides/07-working-with-typescript.md
rename to website/guides/08-working-with-typescript.md
diff --git a/website/webpack.tsx b/website/webpack.tsx
index b92d7c547..b71a386f9 100644
--- a/website/webpack.tsx
+++ b/website/webpack.tsx
@@ -181,7 +181,7 @@ export class Storage {
.resolve(this.dir, name)
.replace(new RegExp("^" + this.dir + "/"), "");
this.files = {...this.files, [name]: "./" + name};
- const stats = await this.run();
+ const stats = await this.run1();
let assets = stats.assetsByChunkName![name];
if (!Array.isArray(assets)) {
assets = [assets];
From 7c4f43e9591122bce38230425a60cca544d78f8c Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Mon, 13 Jul 2020 14:20:51 -0400
Subject: [PATCH 04/26] tweaks
---
README.md | 2 +-
website/build.tsx | 10 +-
...-props.md => 05-special-props-and-tags.md} | 0
website/guides/06-lifecycles.md | 4 -
website/guides/07-reusable-logic.md | 4 +-
website/guides/08-working-with-typescript.md | 1 -
website/guides/10-differences-from-react.md | 83 ---------
website/guides/10-mapping-react-to-crank.md | 157 ++++++++++++++++++
website/src/index.css | 5 +-
9 files changed, 168 insertions(+), 98 deletions(-)
rename website/guides/{05-special-tags-and-props.md => 05-special-props-and-tags.md} (100%)
delete mode 100644 website/guides/10-differences-from-react.md
create mode 100644 website/guides/10-mapping-react-to-crank.md
diff --git a/README.md b/README.md
index 041718b7f..f91808bf1 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Crank uses the same JSX syntax and diffing algorithm popularized by React, allow
### Just Functions
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.
-### Promise-fluent
+### Native Promise Support
Crank provides first-class support for promises. You can use async/await directly in components, and race async components to display fallback UIs.
### Lightweight
diff --git a/website/build.tsx b/website/build.tsx
index b2a01baaa..c5bef1acd 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -197,7 +197,7 @@ function Home(): Element {
-
Declarative components
+
Declarative Components
Crank uses the same JSX syntax and diffing algorithm popularized
by React, allowing you to write HTML-like code directly in your
@@ -212,11 +212,11 @@ function Home(): Element {
-
Promises today
+
Native Promise Support
- Crank provides first-class support for promises. You can use
- async/await directly in components, and race components to display
- fallback UIs.
+ Crank provides first-class support for promises. You can use async
+ functions as components, and race components to display fallback
+ UIs.
diff --git a/website/guides/05-special-tags-and-props.md b/website/guides/05-special-props-and-tags.md
similarity index 100%
rename from website/guides/05-special-tags-and-props.md
rename to website/guides/05-special-props-and-tags.md
diff --git a/website/guides/06-lifecycles.md b/website/guides/06-lifecycles.md
index 975912b8a..7fab4d083 100644
--- a/website/guides/06-lifecycles.md
+++ b/website/guides/06-lifecycles.md
@@ -4,10 +4,6 @@ title: Lifecycles
Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the generator object as components are inserted, updated and removed from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
-## A review of generator functions
-
-TODO
-
## Returning from a generator
Usually, you’ll yield in generator components so that they can continue to respond to updates, but you may want to also `return` a final state. Unlike function components, which are called and returned once for each update, once a generator component returns, it will never update again.
diff --git a/website/guides/07-reusable-logic.md b/website/guides/07-reusable-logic.md
index a64a42b7c..c8323cb19 100644
--- a/website/guides/07-reusable-logic.md
+++ b/website/guides/07-reusable-logic.md
@@ -250,7 +250,7 @@ async function *Counter() {
- Uniform logic to dispose of resources.
**Cons:**
-- Promises and async iterators can be prone to race conditions and deadlocks.
+- Promises and async iterators can cause race conditions and deadlocks, without any language-level features to help you debug them.
**Use cases:**
-If you use async iterators/generators already, Crank is the perfect fit for your application.
+If you use async iterators/generators already, Crank is the perfect framework for your application.
diff --git a/website/guides/08-working-with-typescript.md b/website/guides/08-working-with-typescript.md
index 77dd43e6f..7cb340271 100644
--- a/website/guides/08-working-with-typescript.md
+++ b/website/guides/08-working-with-typescript.md
@@ -124,4 +124,3 @@ function MyButton (props) {
## Typing provisions
By default, calls to `Context.prototype.get` and `Context.prototype.set` will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.
-
diff --git a/website/guides/10-differences-from-react.md b/website/guides/10-differences-from-react.md
deleted file mode 100644
index f454b266c..000000000
--- a/website/guides/10-differences-from-react.md
+++ /dev/null
@@ -1,83 +0,0 @@
----
-title: Differences from React
----
-
-Though Crank is very much inspired by and similar to React, exact compatibility is a non-goal, and we’ve used this as opportunity to “fix” a lot of pain points with React which bothered us over the years. The following is a list of differences with React, as well as justifications for why we chose to implement features differently.
-
-## No classes
-Crank uses functions, generator functions and async functions to implement all of what React implements with classes. Here for instance, is the entirety of the React class-based API implemented with a single async generator function:
-
-```jsx
-async function *ReactComponent(props) {
- let state = componentWillMount(props);
- let ref = yield render(props, state);
- state = componentDidMount(props, state, ref);
- try {
- for await (const newProps of this) {
- if (shouldComponentUpdate(props, newProps, state, ref)) {
- state = componentWillUpdate(props, newProps, state, ref);
- ref = yield render(props, state);
- state = componentDidUpdate(props, newProps, state, ref);
- props = newProps;
- }
- }
- } catch (err) {
- componentDidCatch(err);
- } finally {
- componentWillUnmount(ref);
- }
-}
-```
-
-## No hooks
-Crank does not implement hooks. Hooks bad.
-
-## No `setState` or `useState`
-React has always tightly coupled component updates with local state. Because Crank uses generator functions, state is just local variables, and you can call `this.refresh()` to update the UI to match state. Decoupling these two concerns allows for more nuanced updates to local state without `shouldComponentUpdate` hacks, and is easier to reason about than relying on the framework to provide local state.
-
-## No `Suspense`
-The project known as React `Suspense` is both sub-optimal and likely to be vaporware. It relies on the unusual mechanism of throwing promises, has the hard requirement of a caching mechanism, and is generally difficult to reason about. By leveraging async functions and async generators, Crank allows you to implement the `Suspense` element in user space. No argument from the React team about the necessity of `Suspense` will ever justify it over the convenience provided by being able to use the `await` operator directly in components.
-
-## Props match HTML attributes rather than JS APIs
-### `for` not `htmlFor`, `class` not `className`
-
-Crank does not place any restrictions on the names of JSX props. This means that you can write JSX like ``.
-## style can be a `cssText` string, style object uses snake-case, and `px` is not magically added to numbers.
-```jsx
-
Hello
-```
-## No “controlled”/“uncontrolled”, “value”/“defaultValue” components.
-If you don’t want your component to be updated, don’t update it.
-## No `dangerouslySetInnerHTML={{__html}}` props.
-Just use the `innerHTML` prop. React doesn’t do anything to make setting `innerHTML` safe; they just make you type more and search for the exact spelling of `dangerouslySetInnerHTML` every month or so.
-## Fragments can have `innerHTML`.
-TKTKTK update for Raw
-Fragment behavior can be overridden by renderers, and both the DOM and HTML renderers allow fragments to accept an innerHTML prop, allowing arbitrarily HTML to be written without a parent.
-## Portals are just a special element tag.
-Their behavior is defined by renderers, and all element trees are wrapped implicitly or explicitly in a root portal.
-### No `componentDidUpdate` or `React.memo`.
-Crank uses the special `Copy` element to prevent child updates.
-## No `React.cloneElement`
-Elements are just plain-old JavaScript objects, and there is no need to use a special method to copy them. You can re-use the same elements within generators, mutate them, or use spread syntax to shallowly copy them. Crank will handle reused elements gracefully.
-## No event props
-Event props in React are terrible for the following reasons:
- The EventTarget API takes more than just a function, it also takes options which allow you to register event listeners in the `capture` phase, register `passive` listeners, or register event listeners `once`. React has attempted to allow event handlers to be registered in the capture phase by adding props like `onClickCapture`, but embedding all these options in the prop name would be madness (`onClickCaptureOncePassive`). By emulating the event target API, Crank provides the full power of the `EventTarget` API.
-## Stop doxxing event listeners.
-Event listeners are difficult to name and make the most sense as anonymous functions which are made meaningful by the `type` it’s associated with. React developers often adopt a naming scheme to cache event listeners on component instances like `this.handleClick` to avoid `PureComponent`/`componentDidUpdate` de-optimizations, but if they only had to be defined once, we wouldn’t have to do this. Generator functions naturally allow components to define anonymous event listeners once when the component mounts, and the event target API provided by Crank automatically unregisters these listeners when components are thrown away. This means you never have to reason about when these functions are redefined or what they are capturing in their closures.
-## Custom events are events, and they can be prevented or bubbled like regular DOM events.
-When attempting to define custom event handler props in React, React developers will typically mimic the component props API and allow callbacks to be passed into the component, which the component author will then call directly when they want to trigger the event. This is a painful to do, because you often have to make sure the callback is defined on props, because they are usually optional, and then React developers will also arbitrarily pass data to the callback which is not an event, making custom `onWhatever` props disanalogous with DOM event props, because DOM event props call callbacks with an event. There is no standard for what event-like callback props are called with in React, and there is no way for components to allow parents to prevent default behavior by calling `ev.preventDefault` as you would with a regular DOM event. Worst of all, these props must be passed directly from parent to child, so if a component wants to listen to an event in a deeply nested component, event handlers must either be passed using React contexts, or passed explicitly through each component layer, at each layer renamed to make sense for each nested component API.
-
-Crank avoids this sitution by mimicking the DOM EventTarget API, and allowing developers to create and bubble real `Event` or `CustomEvent` objects with `this.dispatchEvent`. These events can be namespaced, typed, and components can allow parents to cancel events.
-## No refs
- React’s `ref` API has undergone multiple revisions over the years and it’s only gotten more confusing/difficult to use. Crank passes rendered DOM nodes and strings back into generator functions, so you can access them by reading the result of `yield` expressions in generators. You can assign these “refs” to local variables and treat them as you would any local variable without worry.
-## Children can contain any kind of iterable, not just arrays.
- There’s no reason to restrict children in JSX elements to just arrays. You can interpolate ES6 Maps, Sets or any other user-defined iterable into your Crank elements, and Crank will simply render them in an implicit `Fragment`.
-## Keys
-### key has been named to `crank-key`.
-In React, the `key` prop is special and erased from the props visible to components. Insofar as `key` is a common word, Crank namespaces `key` as `crank-key`.
-### The elements of iterables don’t require unique keys.
-Pestering the user to add unique keys to every element of an array is not something Crank will ever do, insofar as most of the time, developers just set the `key` to an `index` into the array. If the developer needs state to be preserved, they will eventually discover that it isn’t preserved in the course of normal development and add a key.
-## No render props
-Crank strongly discourages the React idiom of passing a function as children (or any other prop for that matter. “Render props” produce ugly and non-sensical syntax, and were a direct response to the lack of composability of React class components. Most if not all “render prop” patterns are easier to implement in Crank with the use of higher-order components which are passed component functions and props and produce elements of their own accord.
-## Contexts
-A feature equivalent to React Contexts is planned but not yet implemented.
diff --git a/website/guides/10-mapping-react-to-crank.md b/website/guides/10-mapping-react-to-crank.md
new file mode 100644
index 000000000..a3c81a241
--- /dev/null
+++ b/website/guides/10-mapping-react-to-crank.md
@@ -0,0 +1,157 @@
+---
+title: Mapping React to Crank
+unpublished: true
+---
+
+Though Crank is inspired by React, compatibility is a non-goal, and certain concepts may be implemented using different, non-compatible APIs. The following is a reference for React developers to help them map React concepts APIs to their equivalents in Crank.
+
+## Class Components
+Crank uses functions exclusively for components; it does not provide a class-based component API. You can emulate most of React’s Component class API with the natural lifecycle of an async generator function:
+
+```jsx
+async function *ReactComponent(props) {
+ let state = componentWillMount(props);
+ let ref = yield render(props, state);
+ state = componentDidMount(props, state, ref);
+ try {
+ for await (const newProps of this) {
+ if (shouldComponentUpdate(props, newProps, state, ref)) {
+ state = componentWillUpdate(props, newProps, state, ref);
+ ref = yield render(props, state);
+ state = componentDidUpdate(props, newProps, state, ref);
+ props = newProps;
+ } else {
+ yield ;
+ }
+ }
+ } catch (err) {
+ componentDidCatch(err);
+ } finally {
+ componentWillUnmount(ref);
+ }
+}
+```
+
+This is pseudocode which demonstrates where the methods of React would be called relative to an async generator component. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.
+
+### `setState`
+Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](#TTKTKTKTKTKTKTK).
+
+### `forceUpdate`
+Crank is not “reactive” in the same sense as React, in that it does not actually know when your component’s local state is updated. You can either use `Context.prototype.refresh` to manually refresh the component, much like `forceUpdate`, or you can use async generator components, which refresh automatically whenever the async generator object fulfills.
+
+### `defaultProps`
+Crank doesn’t have a `defaultProps` implementation. Instead, you can provide default values when destructuring props.
+
+### `shouldComponentUpdate`
+Components themselves do not provide a way to prevent updates to themselves. Instead, you can use `Copy` elements to prevent the rerendering of a specific subtree. [Refer to the description of `Copy` elements](#TTKTKTK) for more information.
+
+### `componentWillMount` and `componentDidMount`
+Setup code can simply be written at the top of a generator component.
+
+### getDerivedStateFromProps`, `componentWillUpdate` and `getSnapshotBeforeUpdate`
+Code which compares old and new props or state and performs side-effects can be written directly in your components. See the section on [`prop updates`](#TK) for examples of comparing old and new props. Additionally, check out the [`Context.prototoype.schedule`](#TKTKTK), which takes a callback which is called whenever the component commits.
+
+### `componentWillUnmount`
+You can use `try`/`finally` to run code when the component is unmounted. You can also use the [`Context.prototype.cleanup`] method if you’re writing extensions which don’t run in the main execution of the component.
+
+### `componentDidUpdate`
+Crank uses the special `Copy` element to prevent child updates. See the guide on the `Copy` element to see how you might reimplement `React.memo` directly in user space.
+
+### Error Boundaries/`componentDidCatch`
+To catch errors which occur in child components, you can use generator components and wrap `yield` operations in a `try`/`catch` block. Refer to [the relevant section in guides](#TK).
+
+## Hooks
+Crank does not implement any APIs similar to React Hooks. The following are alternatives to specific hooks.
+
+### `useState` and `useReducer`
+Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](#TTKTKTKTKTKTKTK).
+
+### `useEffect` and `useLayoutEffect`
+Crank does not have any requirements that rendering should be “pure.” In other words, you can trigger side-effects directly while rendering because Crank does not execute components more times than you might expect.
+
+### `useMemo`/`useCallback`
+TTKTK
+
+### `useImperativeHandle`
+Crank does not yet have a way to access component instances, and parent components should not access child contexts directly. An imperative wrapper which uses web components is planned.
+
+### Custom Hooks
+The main appeal of hooks for library authors is that you can encapsulate shared logic in hooks. Refer to the [guide on reusable logic](./reusable-logic) for some patterns for reusing logic between components.
+
+## Suspense and Concurrent Mode
+Crank does not implement any sort of Suspense-like API. As an alternative, you can use async functions and async generator functions directly. See the [guide on async components](./async-components) for an introduction to async components, as well as a demonstration of how you can implement the `Suspense` component directly in user space.
+
+## `PropTypes`
+Crank is written in TypeScript, and you can add type checking to component elements by typing the props parameter of the component function.
+
+## Fragments and array children
+ There’s no reason to restrict children in JSX elements to just arrays. You can interpolate ES6 Maps, Sets or any other user-defined iterable into your Crank elements, and Crank will simply render them in an implicit `Fragment`.
+
+## `React.cloneElement`
+You can clone elements using the `cloneElement` function.
+
+## `ReactDOM.createPortal`
+The `createPortal` function is replaced by a special `Portal` element, whose behavior and expected props varies according to the target rendering environment. Refer to [the guide on the `Portal` element](#KTKTKTKT) for more information.
+
+## `React.memo`
+You can use `Copy` elements to implement `React.memo` in user space:
+```jsx
+function equals(props, newProps) {
+ for (const name in {...props, ...newProps}) {
+ if (props[name] !== newProps[name]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function memo(Component) {
+ return function *Wrapped({props}) {
+ yield ;
+ for (const newProps of this) {
+ if (equals(props, newProps)) {
+ yield ;
+ } else {
+ yield ;
+ }
+
+ props = newProps;
+ }
+ };
+}
+```
+
+See [the guide on component elements](#TKTKTKTK) for more information.
+
+## DOM props
+### `htmlFor` and `className`
+Crank does not place any restrictions on the names of props. This means that you can write JSX like ``.
+
+### The `style` prop.
+```jsx
+
Hello
+```
+
+The `style` prop value can be a `cssText` string, or an object, similar to React. Unlike React, the CSS property names match the case of their CSS equivalents, and we do not add units to numbers.
+
+### `onevent` props
+Crank provides `onevent` props, but they work using the DOM `onevent` props. Crank also provides an `EventTarget` API for components which adds and removes event listeners from the top-level nodes of each component. In both cases, Crank does not use a synthetic event API or shim events. See [the guide on events for more information](#TKTKTKTKTK).
+
+### Controlled and Uncontrolled Props
+Crank does not have a concept of controlled or uncontrolled props, and does not have `defaultValue` props.
+
+### `dangerouslySetInnerHTML`
+Crank elements accept an `innerHTML` prop. Alternatively, you can use the special `Raw` tag to insert arbitrary HTML strings or even nodes in the tree. [See the guide on the `Raw` element](#TKTK) for more information.
+
+## Keys
+Crank provides keyed rendering via the `crank-key` prop. The prop was renamed because “key” is a common word and because the prop is not passed directly into child objects.
+
+Keys work similarly to the way they do in React. The main difference is that Crank does not require elements which appear in arrays or iterables to be keyed.
+
+## Refs
+Crank provides the callback ref API from React via the `crank-ref` prop. Crank does not TKTKTKT
+
+## React Contexts
+TKTKTKTKT
diff --git a/website/src/index.css b/website/src/index.css
index bd404827b..9aafe0598 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -247,19 +247,20 @@ li {
pre {
padding: .8em;
- background: #e1e4ea;
margin: 1.5em 0;
font-size: .9rem;
+ background: #e1e4ea;
color: #0b2f5b;
overflow: auto;
tab-size: 2;
}
:not(pre) > code {
- background: #2b303c;
padding: 0 .1em;
margin: 0;
display: inline;
+ background: #e1e4ea;
+ color: #0b2f5b;
}
.token.comment,
From 272d332a383e17bc045d94354160e9a6f8926be2 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Mon, 13 Jul 2020 19:36:07 -0400
Subject: [PATCH 05/26] some tweaks, moving events to its own file
---
website/blog/2020-04-15-introducing-crank.md | 4 +-
website/guides/02-elements.md | 24 +--
website/guides/03-components.md | 156 +++---------------
website/guides/04-handling-events.md | 130 +++++++++++++++
...c-components.md => 05-async-components.md} | 0
...d-tags.md => 06-special-props-and-tags.md} | 0
.../{06-lifecycles.md => 07-lifecycles.md} | 0
...reusable-logic.md => 08-reusable-logic.md} | 0
...cript.md => 09-working-with-typescript.md} | 0
...om-renderers.md => 10-custom-renderers.md} | 0
...-crank.md => 11-mapping-react-to-crank.md} | 12 +-
...1-api-reference.md => 12-api-reference.md} | 0
12 files changed, 170 insertions(+), 156 deletions(-)
create mode 100644 website/guides/04-handling-events.md
rename website/guides/{04-async-components.md => 05-async-components.md} (100%)
rename website/guides/{05-special-props-and-tags.md => 06-special-props-and-tags.md} (100%)
rename website/guides/{06-lifecycles.md => 07-lifecycles.md} (100%)
rename website/guides/{07-reusable-logic.md => 08-reusable-logic.md} (100%)
rename website/guides/{08-working-with-typescript.md => 09-working-with-typescript.md} (100%)
rename website/guides/{09-custom-renderers.md => 10-custom-renderers.md} (100%)
rename website/guides/{10-mapping-react-to-crank.md => 11-mapping-react-to-crank.md} (95%)
rename website/guides/{11-api-reference.md => 12-api-reference.md} (100%)
diff --git a/website/blog/2020-04-15-introducing-crank.md b/website/blog/2020-04-15-introducing-crank.md
index f8649928d..22b1cfebd 100644
--- a/website/blog/2020-04-15-introducing-crank.md
+++ b/website/blog/2020-04-15-introducing-crank.md
@@ -18,9 +18,9 @@ I like JSX. I like the small surface area it provides compared to template langu
## Tired of the suspense
-The tipping point for me was React’s perennially unready `Suspense` API, React’s solution for async rendering. For the most part, I ignored talks and articles describing Suspense, partially because the React team kept signaling that the API was in flux, but mainly because most discussions of Suspense just seemed to go over my head. I assumed they were working it out, and we’d eventually have something like async/await for React components, so I continued to incorporate React into my projects without thinking too hard about the future of React and promises.
+The tipping point for me was React’s perennially unready Suspense API, React’s solution for async rendering. For the most part, I ignored talks and articles describing Suspense, partially because the React team kept signaling that the API was in flux, but mainly because most discussions of Suspense just seemed to go over my head. I assumed they would work it out, and we’d eventually have something like async/await for React components, so I continued to incorporate React into my projects without thinking too hard about the future of React and promises.
-This was until I decided to explore the Suspense API for myself, when I was trying to create a React hook for usage with async iterators. I had created an async iterator library that I was proud of ([Repeater.js](https://github.com/repeaterjs/repeater)), and I wanted to figure out a way to increase adoption, not just of the library, but also of async iterators in general. The answer seemed logical: create a React hook! At the time, it seemed like every API in existence was being transformed into a React hook, and I thought it would be nice for there to be hooks which allowed developers to use async iterators within React components as well.
+This was until I decided to explore the Suspense API for myself, when I was trying to create a React hook for usage with async iterators. I had created an async iterator library that I was proud of ([Repeater.js](https://github.com/repeaterjs/repeater)), and I wanted to figure out a way to increase adoption, not just of the library, but also of async iterators in general. The answer seemed logical: create a React hook! At the time, it seemed like every API in existence was being transformed into a React hook somehow, and I thought it would be nice for there to be hooks which allowed developers to use async iterators within React components as well.
The result of this effort is [available on GitHub](https://github.com/repeaterjs/react-hooks), and the library is usable, but I mostly abandoned the effort and any sort of greenfield React development when I came to understand what Suspense was and how unwieldy it would have been to incorporate Suspense into the hooks I had written. As of April 2020, the mechanism behind Suspense is for components which make async calls to throw a promise while rendering to indicate that the component is doing something asynchronously. “Throw” as in the way you would “throw” an error in JavaScript with the `throw` operator. In short, React will attempt to render your components, and if a thenable is thrown in the course of rendering, React will catch it in a special parent component called `Suspense`, render a fallback if sufficient time has elapsed, and when the promise has fulfilled, attempt to render the component again. I say “as of April 2020,” because the React team has consistently said the exact details of the Suspense API might change and has used this declaration to preempt any possible criticisms of this mechanism. However, as far as I can tell, that’s how it will work, and how everyone who has written libraries featuring Suspense assumes it will work.
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index 6791772cb..ef3744321 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -5,7 +5,7 @@ title: JSX, Elements and Renderers
**Note for React developers:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to the section on components. Elements in Crank work almost exactly as they do in React.
-Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. Crank is designed to work with transpilers like Babel and TypeScript out-of-box; all you need to do is enable JSX parsing, import the `createElement` function, and include a `@jsx` comment directive (`/** @jsx createElement */`). The parser will then transpile JSX into `createElement` calls. For example, in the code below, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
+Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box; all you need to do is enable JSX parsing, import the `createElement` function, and include a `@jsx` comment directive (`/** @jsx createElement */`). The parser will then transpile JSX into `createElement` calls. For example, in the code below, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
```jsx
/** @jsx createElement */
@@ -16,7 +16,7 @@ const el =
An element
;
const el1 = createElement("div", {id: "element"}, "An element");
```
-The `createElement` function returns an *element*, a plain old JavaScript object. Elements on their own don’t do anything special; rather, Crank provides special classes called *renderers* which interpret elements to produce and manage DOM nodes, HTML strings, canvas scene graphs, or whatever else you can think of. Crank ships with two renderers for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces on the client and HTML responses on the server.
+The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; rather, Crank provides special classes called *renderers* which interpret elements to produce and manage DOM nodes, HTML strings, canvas scene graphs, or whatever else you can think of. Crank ships with two renderers for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
```jsx
/** @jsx createElement */
@@ -35,24 +35,24 @@ console.log(HTMLRenderer.render(el)); //
Hello world

-An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed contents and mostly still work. The advantage of using JSX is that it allows arbitrary JavaScript expressions to be interpolated into elements as its tag, props or children, meaning you can use syntax which looks like HTML seamlessly within JavaScript.
+An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed contents and mostly still work. The advantage of using JSX is that it allows you to interpolate JavaScript expressions as its tag, props or children.
### Tags
```jsx
const intrinsicEl = ;
// transpiles to:
-const intrinsicEl1 = createElement("div");
+const intrinsicEl1 = createElement("div", null);
const componentEl = ;
// transpiles to:
-const componentEl1 = createElement(Component);
+const componentEl1 = createElement(Component, null);
```
-Tags are the first part of an element expression, surrounded by angle brackets, and can be thought of as the name or type of the element. By convention, JSX parsers treat lower-cased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. In Crank, we call elements with string tags *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined by the execution of the referenced function and not the renderer. Elements with functions for tags are called *component elements*.
+Tags are the first part of an element expression, and can be thought of as the “name” or “type” of the element. It is transpiled to the first argument of a `createElement` call. By convention, JSX parsers treat lower-cased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. In Crank, we call elements with string tags *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined by the execution of the referenced function and not the renderer. Elements with functions for tags are called *component elements*.
### Props
-The attribute-like `key="value"` syntax in JSX is transpiled to a single object for each element. We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript value if the value is placed within curly brackets (`key={value}`). Props are used by both intrinsic and component elements to pass values into them and can be thought of as the named arguments to the tag.
+The attribute-like `key="value"` syntax in JSX is transpiled to a single object for each element, and is passed as the second argument to the resulting `createElement` call. We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript value if the value is placed within curly brackets (`key={value}`). Props are used by both intrinsic and component elements to “pass” values into them, similar to how you might pass values into functions as arguments when invoking them.
```jsx
const myClass = "my-class";
@@ -63,7 +63,7 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```
-If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) and can be useful when writing components which modify just one or two props.
+If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).
```jsx
const props = {id: "1", src: "https://example.com/image", alt: "An image"};
@@ -73,7 +73,7 @@ const el1 = createElement("img", {...props, id: "2"});
```
### Children
-As with HTML, Crank elements can have contents, placed between its opening and closing tags. These contents are referred to as *children*. Because elements can have children which are also elements, elements can form a nested tree of nodes.
+As with HTML, Crank elements can have contents, placed between its opening and closing tags. These contents are referred to as *children*. Because elements can have children which are also elements, they form a nested tree of nodes which we call the *element tree*.
```jsx
const list = (
@@ -91,7 +91,7 @@ const list1 = createElement("ul", null,
console.log(list.props.children.length); // 2
```
-By default, the contents of JSX are interpreted as strings, but you can use curly brackets just as we did with props to interpolate arbitrary JavaScript expressions into an element’s children. Besides elements, almost every value in JavaScript can participate in the element tree. Strings and numbers are rendered as text, while the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
+As you can see in the example, the contents of elements which are not themselves part of element expressions are interpreted as strings. However, just as with props, you can use curly brackets to interpolate JavaScript expressions into an element’s children. Besides elements and strings, almost every value in JavaScript can participate in an element tree. Numbers are rendered as strings, and the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
```jsx
const el =
```
-Crank also allows iterables of values to be inserted, so, for instance, you can interpolate an array or a set of elements into an element tree.
+Crank also allows arbitrarily nested iterables of values to be inserted, so, for instance, you can interpolate an array or a set of values into an element tree.
```jsx
const arr = [1, 2, 3];
@@ -110,7 +110,7 @@ console.log(document.body.innerHTML); // "
123 abc
"
```
## Element diffing
-Crank uses the same “virtual DOM diffing” algorithm made popular by React, where DOM operations and component state is determined by the element’s tag and position in the tree. This approach allows you to write declarative code which focuses on producing the right elements, while Crank does the dirty work of managing stateful components and mutating the DOM.
+Crank uses the same “virtual DOM diffing” algorithm made popular by React, where we compare elements by tag and position to reduce and reuse DOM mutations. This approach allows you to write declarative code which focuses on producing the right element tree, while Crank does the dirty work of managing state and mutating the DOM.
```jsx
renderer.render(
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index c7f503bdc..69a196400 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -2,9 +2,11 @@
title: Components
---
-So far, we’ve only seen and used intrinsic elements, but eventually, we’ll want to group parts of the element tree into reusable *components.* In Crank, all components are just functions; there is no class-based interface.
+So far, we’ve only seen and used host elements, but eventually, we’ll want to group parts of the element tree into reusable *components.* In Crank, all components are functions; there is no class-based component API.
## Basic Components
+The simplest kind of component you can write is a *sync function component*. When rendered, the function is invoked with the props object of the element as its first argument, and the return value of the function is recursively rendered as the element’s children.
+
```js
function Greeting({name}) {
return
"
```
-The simplest kind of component you can write is a *sync function component*. When rendered, the function is invoked with the props object of the element as its first parameter, and the return value, usually an element, is recursively rendered by the renderer as the element’s children.
-
-As seen in the example above, you can use [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. Additionally, you can assign default values to each prop using JavaScript’s default value syntax:
-
-```js
-function Greeting({name="World"}) {
- return
Hello, {name}
;
-}
-
-renderer.render(, document.body); // "
Hello World
"
-```
-
-Component elements can take children just as intrinsic elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place the children somewhere in the returned element tree. If you don’t use the `children` prop, the `children` passed in will not be rendered.
+Component elements can be passed children just as host elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place the children somewhere in the returned element tree. If you don’t use the `children` prop, the `children` passed in will be ignored.
```js
function Greeting({name, children}) {
@@ -47,6 +37,16 @@ renderer.render(
console.log(document.body.innerHTML); // "
Message for Nemo: Howdy
"
```
+As seen in the examples above, you can use [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. This means you can assign default values to each prop using JavaScript’s default value syntax:
+
+```js
+function Greeting({name="World"}) {
+ return
Hello, {name}
;
+}
+
+renderer.render(, document.body); // "
Hello World
"
+```
+
## Stateful Components
Eventually, you’re going to want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so.
@@ -82,10 +82,10 @@ console.log(document.body.innerHTML);
// "
You have updated this component 1 time
"
```
-Because we’re now yielding elements rather than returning them, we can make components stateful using variables in the local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered by the renderer, just as if it were returned in a sync function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of the generator is preserved between renders. This allows local state to be encapsulated within the generator’s scope.
+Because we’re now yielding elements rather than returning them, we can make components stateful using variables in the local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered, just as if it were returned in a sync function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of the generator is preserved between renders. This allows local state to be encapsulated within the generator’s scope.
-### Contexts
-The `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` value of each component. These contexts provide several utility methods, most important of which is `this.refresh`, which tells Crank to update the component in place. For generator components, this means that the generator associated with the context will resume so it can yield another value.
+### Contexts
+In the above example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` value of each component. These contexts provide several utility methods, most important of which is `this.refresh`, which tells Crank to update the related component in place. For generator components, Crank resumes the generator component so it can yield another value.
```jsx
function *Timer() {
@@ -112,7 +112,7 @@ This `Timer` component is similar to the `Counter` one, except now the state (th
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval`. Crank will call the `return` method on generator components when the element is removed from the tree, so that the finally block executes and `clearInterval` is called. In this way, you can use the natural lifecycle of a generator to write setup and teardown logic for components, all within the same scope.
### Prop updates
-The generator components we’ve seen so far haven’t used the props object. Generator components can accept props as its first parameter just like regular function components.
+The generator components we’ve seen so far haven’t used props. Generator components can accept props as its first parameter just like regular function components.
```jsx
function *LabeledCounter({message}) {
@@ -132,7 +132,7 @@ renderer.render(, docu
console.log(document.body.innerHTML); // "
The count is now: 3
"
```
-This mostly works, except we have a bug where the component kept yielding the initial message even though a new message was passed in via props. To fix this, we can make sure props are kept up to date by iterating over the context:
+This mostly works, except now we have a bug where the component kept yielding the initial message even though a new message was passed in via props. To fix this, we can make sure props are kept up to date by iterating over the context:
```jsx
function *Counter({message}) {
@@ -153,7 +153,7 @@ console.log(document.body.innerHTML); // "
What if I update the message: 2
By replacing the `while (true)` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to elements.
-One idiom we see in the example above is the overwriting of variables declared via the generator’s parameters (`message`) with the `for…of` loop. This allows those variables to always remain in sync with the current props passed to each component. However, there is no reason you have to always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
+One idiom we see in the example above is that we overwrite the variables declared via the generator’s parameters (`message`) with the `for…of` loop. This allows those variables to always remain in sync with the current props passed to each component. However, there is no reason you have to always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
```jsx
function *Greeting({name}) {
@@ -182,119 +182,3 @@ console.log(document.body.innerHTML); // "
Hello again Bob
"
```
The fact that state is just local variables allows us to blur the lines between props and state, in a way that is easy to understand and without lifecycle methods like `componentWillUpdate` from React. With generators and `for` loops, comparing old and new props is as easy as comparing adjacent elements of an array.
-
-## Interactive components
-Components produce elements, which can be rendered as DOM nodes. Most applications require event listeners to be attached to these nodes so that application state can be updated according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered children:
-
-### The EventTarget interface
-Crank contexts implement the same `EventTarget` interface used by the DOM, and automatically registers and tears down these listeners as DOM nodes are added and removed. You can write interactive components by combining event listeners with local variables and the `this.refresh` method.
-
-```jsx
-function *Clicker() {
- let count = 0;
- this.addEventListener("click", () => {
- count++;
- this.refresh();
- });
-
- while (true) {
- yield (
-
- );
- }
-}
-```
-
-The local state `count` is now updated in the event listener, which triggers when the rendered button is actually clicked. The `this.addEventListener` method only attaches to the top-level node which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
-
-```jsx
-function *Clicker() {
- let count = 0;
- this.addEventListener("click", (ev) => {
- if (ev.target.tagName === "BUTTON") {
- count++;
- this.refresh();
- }
- });
-
- while (true) {
- yield (
-
- The button has been clicked {count} {count === 1 ? "time" : "times"}.
-
-
- );
- }
-}
-```
-
-Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button`.
-
-### DOM onevent props
-As an alternative to event targets, Crank also allows you to attach event callbacks directly on rendered children using event props. These props start with `on`, are all lowercase, and correspond exactly to the properties specified by the DOM’s [GlobalEventHandlers mixin](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers).
-
-```jsx
-function *Clicker() {
- let count = 0;
- const handleClick = () => {
- count++;
- this.refresh();
- };
-
- while (true) {
- yield (
-
- The button has been clicked {count} {count === 1 ? "time" : "times"}.
-
-
- );
- }
-}
-```
-
-The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and can register them on the exact children you’d like to listen to. On the other hand, using `this.addEventListener` allows you to register `passive` listeners, or listeners which trigger during the capturing phase. Crank supports both API styles for convenience and flexibility.
-
-### Dispatching events
-Crank contexts implement the full EventTarget interface, meaning you can use `this.dispatchEvent` and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) constructor to bubble events to parent components:
-
-```jsx
-function MyButton(props) {
- this.addEventListener("click", () => {
- this.dispatchEvent(new CustomEvent("mybutton.click", {
- bubbles: true,
- detail: {id: props.id},
- }));
- });
-
- return (
-
- );
-}
-
-function MyButtons() {
- return [1, 2, 3, 4, 5].map((i) => (
-
Last pressed id: {lastId == null ? "N/A" : lastId}
-
- );
- }
-}
-```
-
-`MyButton` is a function component which wraps a `button` element. It bubbles a `CustomEvent` whose type is `"mybutton.click"` when it is pressed, and whose `detail` property contains data about the id of the clicked button. This event is not bubbled to parent DOM nodes but to parent component contexts, and in the example, it propagates to the context of `MyApp`. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without resorting to state management solutions like Redux from React.
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
new file mode 100644
index 000000000..428abf2cb
--- /dev/null
+++ b/website/guides/04-handling-events.md
@@ -0,0 +1,130 @@
+---
+title: Handling Events
+---
+
+## Interactive components
+Components produce children, which are rendered as DOM nodes by the default DOM renderer. Most applications require event listeners to be attached to these nodes so that application state can be updated according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered children.
+
+### onevent props
+Crank allows you to attach event callbacks directly to host element using onevent props. For the DOM renderer, these props start with `on`, are all lowercase, and correspond exactly to the properties specified by the DOM’s [GlobalEventHandlers mixin](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components:
+
+```jsx
+function *Clicker() {
+ let count = 0;
+ const handleClick = () => {
+ count++;
+ this.refresh();
+ };
+
+ while (true) {
+ yield (
+
+ The button has been clicked {count} {count === 1 ? "time" : "times"}.
+
+
+ );
+ }
+}
+```
+
+### The EventTarget interface
+As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM elements.
+
+```jsx
+function *Clicker() {
+ let count = 0;
+ this.addEventListener("click", () => {
+ count++;
+ this.refresh();
+ });
+
+ while (true) {
+ yield (
+
+ );
+ }
+}
+```
+
+The local state `count` is now updated in the event listener, which triggers when the rendered button is actually clicked.
+
+**NOTE:** When using the `addEventListener` method, you do not have to call the `removeEventListener` method if you merely need to remove event listeners when the component is unmounted. This is done automatically.
+
+## Event delegation
+
+The Context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
+
+```jsx
+function *Clicker() {
+ let count = 0;
+ this.addEventListener("click", (ev) => {
+ if (ev.target.tagName === "BUTTON") {
+ count++;
+ this.refresh();
+ }
+ });
+
+ while (true) {
+ yield (
+
+ The button has been clicked {count} {count === 1 ? "time" : "times"}.
+
+
+ );
+ }
+}
+```
+
+Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button`.
+
+## When to use onevent props and the EventTarget API
+The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and can register them on the exact element you’d like to listen to.
+
+On the other hand, using `this.addEventListener` allows you to take full advantage of the EventTarget API, including registering `passive` events or events which are dispatched during the `capture` phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to the rendered elements at all.
+
+Crank supports both API styles for convenience and flexibility.
+
+### Dispatching events
+Crank contexts implement the full EventTarget interface, meaning you can use the `dispatchEvent` method and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) constructor to bubble events to parent components:
+
+```jsx
+function MyButton(props) {
+ this.addEventListener("click", () => {
+ this.dispatchEvent(new CustomEvent("mybuttonclick", {
+ bubbles: true,
+ detail: {id: props.id},
+ }));
+ });
+
+ return (
+
+ );
+}
+
+function MyButtons() {
+ return [1, 2, 3, 4, 5].map((i) => (
+
Last pressed id: {lastId == null ? "N/A" : lastId}
+
+ );
+ }
+}
+```
+
+`MyButton` is a function component which wraps a `button` element. It bubbles a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the id of the clicked button. This event is not bubbled to parent DOM nodes but to parent component contexts, and in the example, it propagates to the context of `MyApp`, which listens for the event using the `addEventListener` method. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
diff --git a/website/guides/04-async-components.md b/website/guides/05-async-components.md
similarity index 100%
rename from website/guides/04-async-components.md
rename to website/guides/05-async-components.md
diff --git a/website/guides/05-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
similarity index 100%
rename from website/guides/05-special-props-and-tags.md
rename to website/guides/06-special-props-and-tags.md
diff --git a/website/guides/06-lifecycles.md b/website/guides/07-lifecycles.md
similarity index 100%
rename from website/guides/06-lifecycles.md
rename to website/guides/07-lifecycles.md
diff --git a/website/guides/07-reusable-logic.md b/website/guides/08-reusable-logic.md
similarity index 100%
rename from website/guides/07-reusable-logic.md
rename to website/guides/08-reusable-logic.md
diff --git a/website/guides/08-working-with-typescript.md b/website/guides/09-working-with-typescript.md
similarity index 100%
rename from website/guides/08-working-with-typescript.md
rename to website/guides/09-working-with-typescript.md
diff --git a/website/guides/09-custom-renderers.md b/website/guides/10-custom-renderers.md
similarity index 100%
rename from website/guides/09-custom-renderers.md
rename to website/guides/10-custom-renderers.md
diff --git a/website/guides/10-mapping-react-to-crank.md b/website/guides/11-mapping-react-to-crank.md
similarity index 95%
rename from website/guides/10-mapping-react-to-crank.md
rename to website/guides/11-mapping-react-to-crank.md
index a3c81a241..83840e866 100644
--- a/website/guides/10-mapping-react-to-crank.md
+++ b/website/guides/11-mapping-react-to-crank.md
@@ -1,6 +1,5 @@
---
title: Mapping React to Crank
-unpublished: true
---
Though Crank is inspired by React, compatibility is a non-goal, and certain concepts may be implemented using different, non-compatible APIs. The following is a reference for React developers to help them map React concepts APIs to their equivalents in Crank.
@@ -49,7 +48,7 @@ Components themselves do not provide a way to prevent updates to themselves. Ins
### `componentWillMount` and `componentDidMount`
Setup code can simply be written at the top of a generator component.
-### getDerivedStateFromProps`, `componentWillUpdate` and `getSnapshotBeforeUpdate`
+### `getDerivedStateFromProps`, `componentWillUpdate` and `getSnapshotBeforeUpdate`
Code which compares old and new props or state and performs side-effects can be written directly in your components. See the section on [`prop updates`](#TK) for examples of comparing old and new props. Additionally, check out the [`Context.prototoype.schedule`](#TKTKTK), which takes a callback which is called whenever the component commits.
### `componentWillUnmount`
@@ -70,7 +69,7 @@ Crank uses generator functions and local variables for local state. Refer to [th
### `useEffect` and `useLayoutEffect`
Crank does not have any requirements that rendering should be “pure.” In other words, you can trigger side-effects directly while rendering because Crank does not execute components more times than you might expect.
-### `useMemo`/`useCallback`
+### `useMemo` and `useCallback`
TTKTK
### `useImperativeHandle`
@@ -125,7 +124,9 @@ function memo(Component) {
See [the guide on component elements](#TKTKTKTK) for more information.
-## DOM props
+## DOM element props
+The following are a list of the differences in APIs when passings props to DOM elements.
+
### `htmlFor` and `className`
Crank does not place any restrictions on the names of props. This means that you can write JSX like ``.
@@ -140,7 +141,7 @@ The `style` prop value can be a `cssText` string, or an object, similar to React
Crank provides `onevent` props, but they work using the DOM `onevent` props. Crank also provides an `EventTarget` API for components which adds and removes event listeners from the top-level nodes of each component. In both cases, Crank does not use a synthetic event API or shim events. See [the guide on events for more information](#TKTKTKTKTK).
### Controlled and Uncontrolled Props
-Crank does not have a concept of controlled or uncontrolled props, and does not have `defaultValue` props.
+Crank does not have a concept of controlled or uncontrolled props, and does not provide `defaultValue`-style props for DOM elements. Instead, Crank renderers adopt the convention that renderers only mutate the props specified in the . For instance
### `dangerouslySetInnerHTML`
Crank elements accept an `innerHTML` prop. Alternatively, you can use the special `Raw` tag to insert arbitrary HTML strings or even nodes in the tree. [See the guide on the `Raw` element](#TKTK) for more information.
@@ -154,4 +155,3 @@ Keys work similarly to the way they do in React. The main difference is that Cra
Crank provides the callback ref API from React via the `crank-ref` prop. Crank does not TKTKTKT
## React Contexts
-TKTKTKTKT
diff --git a/website/guides/11-api-reference.md b/website/guides/12-api-reference.md
similarity index 100%
rename from website/guides/11-api-reference.md
rename to website/guides/12-api-reference.md
From cbc5d96b48de4f3c27c2a1f597cd376e65f6ea17 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 14 Jul 2020 12:38:21 -0400
Subject: [PATCH 06/26] tweak docs
---
README.md | 6 ++--
website/build.tsx | 40 ++++++++++++++++++---
website/guides/02-elements.md | 2 +-
website/guides/03-components.md | 6 ++--
website/guides/04-handling-events.md | 11 +++---
website/guides/05-async-components.md | 6 ++--
website/guides/06-special-props-and-tags.md | 2 +-
website/guides/07-lifecycles.md | 2 +-
website/src/index.css | 9 +++--
9 files changed, 56 insertions(+), 28 deletions(-)
diff --git a/README.md b/README.md
index f91808bf1..7c717557b 100644
--- a/README.md
+++ b/README.md
@@ -10,17 +10,17 @@ Crank uses the same JSX syntax and diffing algorithm popularized by React, allow
### Just Functions
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.
-### Native Promise Support
+### Promise-friendly
Crank provides first-class support for promises. You can use async/await directly in components, and race async components to display fallback UIs.
### Lightweight
Crank has no dependencies, and its core is a single file. It currently measures at [4.5KB minified and gzipped](https://bundlephobia.com/result?p=@bikeshaving/crank).
### Performant
-[According to synthetic benchmarks](https://krausest.github.io/js-framework-benchmark/current.html), Crank beats React in terms of execution time and memory usage. It‘s current performance is comparable to Preact and Vue.
+[According to a standard benchmark](https://krausest.github.io/js-framework-benchmark/current.html), Crank’s performance beats React’s, and is comparable to that of Preact’s and Vue’s.
### Extensible
-TKTKTK WRITE ABOUT LIBRARY PATTERNS AND CUSTOM RENDERERS.
+The core renderer can be extended to target alternative environments such as WebGL libraries, terminals, smartphones or smart TVs.
## Installation
Crank is available on [NPM](https://npmjs.org/@bikeshaving/crank) in the ESModule and CommonJS formats.
diff --git a/website/build.tsx b/website/build.tsx
index c5bef1acd..30e16a2bf 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -195,12 +195,13 @@ function Home(): Element {
/>
+
Features
Declarative Components
Crank uses the same JSX syntax and diffing algorithm popularized
- by React, allowing you to write HTML-like code directly in your
+ by React, allowing you to write HTML-like code directly in
JavaScript.
@@ -212,11 +213,40 @@ function Home(): Element {
-
Native Promise Support
+
Promise-friendly
- Crank provides first-class support for promises. You can use async
- functions as components, and race components to display fallback
- UIs.
+ Crank provides first-class support for promises. You can use
+ async/await directly in components, and race components to display
+ fallback UIs.
+
+
+
+
Lightweight
+
+ Crank has no dependencies, and its core is a single file. It
+ currently measures at{" "}
+
+ 4.5KB minified and gzipped
+
+ .
+
+
+
+
Performant
+
+
+ According to benchmarks
+
+ , Crank beats React in terms of speed and memory usage, and is
+ comparable to that of Preact or Vue.
+
+
+
+
Extensible
+
+ The core renderer can be extended to target alternative
+ environments such as WebGL libraries, terminals, smartphones or
+ smart TVs.
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index ef3744321..23b6b3e08 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -5,7 +5,7 @@ title: JSX, Elements and Renderers
**Note for React developers:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to the section on components. Elements in Crank work almost exactly as they do in React.
-Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box; all you need to do is enable JSX parsing, import the `createElement` function, and include a `@jsx` comment directive (`/** @jsx createElement */`). The parser will then transpile JSX into `createElement` calls. For example, in the code below, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
+Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box; all you need to do is enable JSX parsing, import the `createElement` function, and include a `@jsx` comment directive (`/** @jsx createElement */`). The parser will then transpile JSX into `createElement` calls. For example, in the following code, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
```jsx
/** @jsx createElement */
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index 69a196400..3558fb90f 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -37,7 +37,7 @@ renderer.render(
console.log(document.body.innerHTML); // "
Message for Nemo: Howdy
"
```
-As seen in the examples above, you can use [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. This means you can assign default values to each prop using JavaScript’s default value syntax:
+You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to each prop using JavaScript’s default value syntax.
```js
function Greeting({name="World"}) {
@@ -85,7 +85,7 @@ console.log(document.body.innerHTML);
Because we’re now yielding elements rather than returning them, we can make components stateful using variables in the local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered, just as if it were returned in a sync function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of the generator is preserved between renders. This allows local state to be encapsulated within the generator’s scope.
### Contexts
-In the above example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` value of each component. These contexts provide several utility methods, most important of which is `this.refresh`, which tells Crank to update the related component in place. For generator components, Crank resumes the generator component so it can yield another value.
+In the preceding example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` value of each component. These contexts provide several utility methods, most important of which is `this.refresh`, which tells Crank to update the related component in place. For generator components, Crank resumes the generator component so it can yield another value.
```jsx
function *Timer() {
@@ -153,7 +153,7 @@ console.log(document.body.innerHTML); // "
What if I update the message: 2
By replacing the `while (true)` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to elements.
-One idiom we see in the example above is that we overwrite the variables declared via the generator’s parameters (`message`) with the `for…of` loop. This allows those variables to always remain in sync with the current props passed to each component. However, there is no reason you have to always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
+One idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters (`message`) with the `for…of` loop. This allows those variables to always remain in sync with the current props passed to each component. However, there is no reason you have to always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
```jsx
function *Greeting({name}) {
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
index 428abf2cb..438f03b9d 100644
--- a/website/guides/04-handling-events.md
+++ b/website/guides/04-handling-events.md
@@ -2,11 +2,10 @@
title: Handling Events
---
-## Interactive components
-Components produce children, which are rendered as DOM nodes by the default DOM renderer. Most applications require event listeners to be attached to these nodes so that application state can be updated according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered children.
+Most DOM applications require some measure of interactivity, where application state updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
-### onevent props
-Crank allows you to attach event callbacks directly to host element using onevent props. For the DOM renderer, these props start with `on`, are all lowercase, and correspond exactly to the properties specified by the DOM’s [GlobalEventHandlers mixin](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components:
+## onevent props
+You can attach event callbacks to host element directly using onevent props. These props start with `on`, are all lowercase, and correspond exactly to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
```jsx
function *Clicker() {
@@ -27,7 +26,7 @@ function *Clicker() {
}
```
-### The EventTarget interface
+## The EventTarget interface
As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM elements.
```jsx
@@ -84,7 +83,7 @@ On the other hand, using `this.addEventListener` allows you to take full advanta
Crank supports both API styles for convenience and flexibility.
-### Dispatching events
+## Dispatching events
Crank contexts implement the full EventTarget interface, meaning you can use the `dispatchEvent` method and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) constructor to bubble events to parent components:
```jsx
diff --git a/website/guides/05-async-components.md b/website/guides/05-async-components.md
index 545444836..934946376 100644
--- a/website/guides/05-async-components.md
+++ b/website/guides/05-async-components.md
@@ -51,7 +51,7 @@ async function Delay ({message}) {
})();
```
-In the example above, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings happen synchronously, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then only the fourth rendering is enqueued because third rendering’s props are obsolete by the time the component is ready to rerender. This behavior allows async components to always be kept up-to-date without producing excess calls to your async functions.
+In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings happen synchronously, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then only the fourth rendering is enqueued because third rendering’s props are obsolete by the time the component is ready to rerender. This behavior allows async components to always be kept up-to-date without producing excess calls to your async functions.
2. If two different async components are rendered in the same position, the components are raced. If the earlier component fulfills first, it shows until the later component fulfills. If the later component fulfills first, the earlier component is never rendered. This ratcheting effect becomes useful for rendering fallback states for async components, as we’ll see later.
@@ -132,7 +132,7 @@ async function *AsyncLabeledCounter ({message}) {
`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in the section on sync generator components, and demonstrates several key differences between sync and async generator components. First, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because Crank contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Second, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed; Rather than suspending at each yield, we rely on the `for await…of` loop, which pauses at the bottom for the next rendering.
## Responsive Loading Indicators
-The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes longer than expected. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described above along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
+The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes longer than expected. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described previously along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
```jsx
async function LoadingIndicator() {
@@ -187,7 +187,7 @@ renderer.render(, document.body);
In this example, the `RandomDogLoader` component is an async generator component which races the `LoadingIndicator` component with the `RandomDog` component. Because the async generator component resumes continuously, both components are executed, and according to the second rule, only the second component shows if it fulfills faster than the first component, which fulfills at a fixed interval of one second.
-The above example hints at how we could abstract this pattern to implement a `Suspense` component, a proposed custom API in React which allows async components with fallback states:
+The preceding example hints at how we could abstract this pattern to implement a `Suspense` component, a proposed custom API in React which allows async components with fallback states:
```jsx
async function Fallback({timeout = 1000, children}) {
diff --git a/website/guides/06-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
index 96f356d31..bb833f41c 100644
--- a/website/guides/06-special-props-and-tags.md
+++ b/website/guides/06-special-props-and-tags.md
@@ -166,7 +166,7 @@ function memo(Component) {
}
```
-In the example above, `memo` is a higher-order component, a function which takes a component and returns a component which compares new and old props and yields a `Copy` element if old and new props are shallowly equal. A `Copy` element can appear anywhere in an element tree to prevent rerenderings, and the only prop `Copy` elements take is the `crank-key` prop, allowing you to copy elements by key rather than position.
+In this example, `memo` is a higher-order component, a function which takes a component and returns a component which compares new and old props and yields a `Copy` element if old and new props are shallowly equal. A `Copy` element can appear anywhere in an element tree to prevent rerenderings, and the only prop `Copy` elements take is the `crank-key` prop, allowing you to copy elements by key rather than position.
### Raw
Sometimes, you may want to insert raw HTML or actual DOM nodes directly into the element tree. Crank allows you to do this with the `Raw` element. The `Raw` element takes a `value` prop which is interpreted by the renderer. For the DOM renderer, if `value` is an HTML string, the renderer will parse and insert the resulting DOM nodes, and if it’s already a DOM node Crank will insert them in place. Be careful when using `Raw` elements, as passing unsanitized text inputs can lead to security vulnerabilities.
diff --git a/website/guides/07-lifecycles.md b/website/guides/07-lifecycles.md
index 7fab4d083..ffba48de4 100644
--- a/website/guides/07-lifecycles.md
+++ b/website/guides/07-lifecycles.md
@@ -137,6 +137,6 @@ async function *MyInput(props) {
}
```
-The above component focuses every time it is rerendered. You might notice that we use an async generator component here. That’s because async generators continuously resume, and rely on the `for await` loop to await new updates.
+The `MyInput` component focuses every time it is rerendered. You might notice that we use an async generator component here. That’s because async generators continuously resume, and rely on the `for await` loop to await new updates.
**TODO: Design APIs/Document them for working with yield expressions in sync generators.**
diff --git a/website/src/index.css b/website/src/index.css
index 9aafe0598..a1c975e37 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -191,7 +191,7 @@ li {
}
.hero {
- min-height: 500px;
+ padding: 7em 0;
display: flex;
flex-direction: column;
justify-content: center;
@@ -234,13 +234,12 @@ li {
@media screen and (min-width: 1100px) {
display: flex;
flex-direction: row;
+ flex-wrap: wrap;
justify-content: space-between;
+
.feature {
+ flex-basis: 32%;
margin: 0;
- max-width: 500px;
- + .feature {
- margin-left: 1rem;
- }
}
}
}
From bfc3b24c97e6ee34aac369cf0b67d275537ae83b Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 14 Jul 2020 19:15:49 -0400
Subject: [PATCH 07/26] filling more docs in
---
website/build.tsx | 8 +--
website/guides/02-elements.md | 16 +++--
website/guides/03-components.md | 13 ++--
website/guides/04-handling-events.md | 12 ++--
website/guides/05-async-components.md | 18 ++---
website/guides/06-special-props-and-tags.md | 79 ++++++++++++++++++---
6 files changed, 107 insertions(+), 39 deletions(-)
diff --git a/website/build.tsx b/website/build.tsx
index 30e16a2bf..e37586123 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -198,7 +198,7 @@ function Home(): Element {
Features
-
Declarative Components
+
Declarative
Crank uses the same JSX syntax and diffing algorithm popularized
by React, allowing you to write HTML-like code directly in
@@ -215,8 +215,8 @@ function Home(): Element {
Promise-friendly
- Crank provides first-class support for promises. You can use
- async/await directly in components, and race components to display
+ Crank provides first-class support for promises. You can define
+ components as async functions and race renderings to display
fallback UIs.
@@ -238,7 +238,7 @@ function Home(): Element {
According to benchmarks
, Crank beats React in terms of speed and memory usage, and is
- comparable to that of Preact or Vue.
+ currently comparable to Preact or Vue.
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index 23b6b3e08..72552226f 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -16,7 +16,7 @@ const el =
An element
;
const el1 = createElement("div", {id: "element"}, "An element");
```
-The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; rather, Crank provides special classes called *renderers* which interpret elements to produce and manage DOM nodes, HTML strings, canvas scene graphs, or whatever else you can think of. Crank ships with two renderers for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
+The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, Crank provides special classes called *renderers* which interpret elements to produce and manage DOM nodes, HTML strings, canvas scene graphs, or whatever else you can think of. Crank ships with two renderers for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
```jsx
/** @jsx createElement */
@@ -35,9 +35,11 @@ console.log(HTMLRenderer.render(el)); //
Hello world

-An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed contents and mostly still work. The advantage of using JSX is that it allows you to interpolate JavaScript expressions as its tag, props or children.
+An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed contents and mostly still work. The advantage of using JSX is that it allows you to interpolate arbitrary JavaScript expressions as an element’s tag, props or children.
### Tags
+Tags are the first part of an element expression, and can be thought of as the “name” or “type” of the element. JSX parsers will transpile the tag name as the first argument of a `createElement` call.
+
```jsx
const intrinsicEl = ;
// transpiles to:
@@ -48,11 +50,11 @@ const componentEl = ;
const componentEl1 = createElement(Component, null);
```
-Tags are the first part of an element expression, and can be thought of as the “name” or “type” of the element. It is transpiled to the first argument of a `createElement` call. By convention, JSX parsers treat lower-cased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. In Crank, we call elements with string tags *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined by the execution of the referenced function and not the renderer. Elements with functions for tags are called *component elements*.
+By convention, JSX parsers treat lower-cased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. We call elements with string tags *host* or *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined by the execution of the referenced function and not the renderer. Elements with function tags are called *component elements*.
### Props
-The attribute-like `key="value"` syntax in JSX is transpiled to a single object for each element, and is passed as the second argument to the resulting `createElement` call. We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript value if the value is placed within curly brackets (`key={value}`). Props are used by both intrinsic and component elements to “pass” values into them, similar to how you might pass values into functions as arguments when invoking them.
+JSX parsers coalesce the attribute-like `key="value"` syntax to a single object for each element, and pass this object as the second argument to the resulting `createElement` call.
```jsx
const myClass = "my-class";
@@ -63,6 +65,8 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```
+We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript value if the value is placed within curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you might pass arguments into functions when invoking them.
+
If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).
```jsx
@@ -91,7 +95,7 @@ const list1 = createElement("ul", null,
console.log(list.props.children.length); // 2
```
-As you can see in the example, the contents of elements which are not themselves part of element expressions are interpreted as strings. However, just as with props, you can use curly brackets to interpolate JavaScript expressions into an element’s children. Besides elements and strings, almost every value in JavaScript can participate in an element tree. Numbers are rendered as strings, and the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
+As you can see in the example, JSX parsers interpret the contents of an element which fall outside elements as strings. However, just as with props, you can use curly brackets to interpolate JavaScript expressions into an element’s children. Besides elements and strings, almost every value in JavaScript can participate in an element tree. Numbers are rendered as strings, and the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
```jsx
const el =
{"a"}{1 + 1}{true}{false}{null}{undefined}
;
@@ -133,3 +137,5 @@ renderer.render(
console.log(document.body.firstChild === div); // true
console.log(document.body.firstChild.firstChild === span); // true
```
+
+**NOTE:** We usually avoid using the term “virtual DOM” in Crank, insofar as the Crank renderer can render to multiple environments; instead, we use the term “element diffing” to mean mostly the same thing.
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index 3558fb90f..3fec69046 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -16,7 +16,7 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // "
Hello World
"
```
-Component elements can be passed children just as host elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place the children somewhere in the returned element tree. If you don’t use the `children` prop, the `children` passed in will be ignored.
+Component elements can be passed children just as host elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place the children somewhere in the returned element tree. If you don’t use the `children` prop, it will be ignored.
```js
function Greeting({name, children}) {
@@ -37,7 +37,7 @@ renderer.render(
console.log(document.body.innerHTML); // "
Message for Nemo: Howdy
"
```
-You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to each prop using JavaScript’s default value syntax.
+You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to a specific prop by using JavaScript’s default value syntax.
```js
function Greeting({name="World"}) {
@@ -48,7 +48,7 @@ renderer.render(, document.body); // "
Hello World
"
```
## Stateful Components
-Eventually, you’re going to want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so.
+Eventually, you’re going to want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so. These types of components are referred to as *sync generator components*.
```jsx
function *Counter() {
@@ -85,7 +85,7 @@ console.log(document.body.innerHTML);
Because we’re now yielding elements rather than returning them, we can make components stateful using variables in the local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered, just as if it were returned in a sync function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of the generator is preserved between renders. This allows local state to be encapsulated within the generator’s scope.
### Contexts
-In the preceding example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` value of each component. These contexts provide several utility methods, most important of which is `this.refresh`, which tells Crank to update the related component in place. For generator components, Crank resumes the generator component so it can yield another value.
+In the preceding example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component in place.
```jsx
function *Timer() {
@@ -107,7 +107,7 @@ function *Timer() {
}
```
-This `Timer` component is similar to the `Counter` one, except now the state (the local variable `seconds`) is updated in the callback passed to `setInterval`, rather than when the component is rerendered. The `this.refresh` method is called to ensure that the generator is stepped through each interval, so that the rendered DOM actually reflects the updated `seconds` variable.
+This `Timer` component is similar to the `Counter` one, except now the state (the local variable `seconds`) is updated in the callback passed to `setInterval`, rather than when the component is rerendered. Additionally, `refresh` method is called to ensure that the generator is stepped through each interval, so that the rendered DOM actually reflects the updated `seconds` variable.
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval`. Crank will call the `return` method on generator components when the element is removed from the tree, so that the finally block executes and `clearInterval` is called. In this way, you can use the natural lifecycle of a generator to write setup and teardown logic for components, all within the same scope.
@@ -127,6 +127,7 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // "
What if I update the message: 2
By replacing the `while (true)` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to elements.
-One idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters (`message`) with the `for…of` loop. This allows those variables to always remain in sync with the current props passed to each component. However, there is no reason you have to always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
+One idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters (`message`) with the destructuring expression in the `for…of` statement. This is an easy way to make sure those variables stay in sync with the current props of the component. However, there is no requirement that you must always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
```jsx
function *Greeting({name}) {
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
index 438f03b9d..b6bd0876f 100644
--- a/website/guides/04-handling-events.md
+++ b/website/guides/04-handling-events.md
@@ -2,7 +2,7 @@
title: Handling Events
---
-Most DOM applications require some measure of interactivity, where application state updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
+Most DOM applications require some measure of interactivity, where the user interface updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
## onevent props
You can attach event callbacks to host element directly using onevent props. These props start with `on`, are all lowercase, and correspond exactly to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
@@ -27,7 +27,7 @@ function *Clicker() {
```
## The EventTarget interface
-As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM elements.
+As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM.
```jsx
function *Clicker() {
@@ -74,12 +74,12 @@ function *Clicker() {
}
```
-Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button`.
+Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button` element.
## When to use onevent props and the EventTarget API
-The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and can register them on the exact element you’d like to listen to.
+The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and you can register them on the exact element you’d like to listen to.
-On the other hand, using `this.addEventListener` allows you to take full advantage of the EventTarget API, including registering `passive` events or events which are dispatched during the `capture` phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to the rendered elements at all.
+On the other hand, using `this.addEventListener` allows you to take full advantage of the EventTarget API, including registering passive event listeners or events which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to the produced elements.
Crank supports both API styles for convenience and flexibility.
@@ -126,4 +126,4 @@ function *MyApp() {
}
```
-`MyButton` is a function component which wraps a `button` element. It bubbles a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the id of the clicked button. This event is not bubbled to parent DOM nodes but to parent component contexts, and in the example, it propagates to the context of `MyApp`, which listens for the event using the `addEventListener` method. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
+`MyButton` is a function component which wraps a `button` element. It dispatches a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the id of the clicked button. This event is not triggered or bubbled on the underlying DOM nodes; instead, it can be listened for by parent component contexts using event bubbling, and in the example, the event propagates and is handled by the `MyApp` component. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
diff --git a/website/guides/05-async-components.md b/website/guides/05-async-components.md
index 934946376..83354f756 100644
--- a/website/guides/05-async-components.md
+++ b/website/guides/05-async-components.md
@@ -3,7 +3,7 @@ title: Async Components
---
## Async function components
-So far, every component we’ve seen has worked synchronously, and Crank will respect this as an intentional decision by rendering your components synchronously from start to finish. However, modern JavaScript includes promises and `async`/`await`, allowing you to write concurrently executing code as if it were synchronous. To facilitate this style of code, Crank allows components to be asynchronous functions as well.
+So far, every component we’ve seen has worked synchronously, and Crank will respect this as an intentional decision by keeping the entire process of rendering synchronous from start to finish. However, modern JavaScript includes promises and `async`/`await`, which allows you to write concurrently executing code as if it were synchronous. To facilitate this style of code, Crank allows components to be asynchronous functions as well.
```jsx
async function IPAddress () {
@@ -18,10 +18,10 @@ async function IPAddress () {
})();
```
-When a Crank renderer runs a component which returns a promise, the process of rendering becomes asynchronous as well. Concretely, this means that `renderer.render` itself will return a promise which fulfills when all async calls in the element tree have fulfilled at least once, and nothing will be added to the DOM until this happens.
+When a Crank renderer runs a component which returns a promise, the rendering process becomes asynchronous as well. Concretely, this means that `renderer.render` call will return a promise which fulfills when all async components in the element tree have fulfilled at least once, and that no actual DOM updates will be triggered until this happens.
### Concurrent updates
-Because rendering can happen concurrently while async function components in the tree are still pending, Crank implements a couple rules to make concurrent updates predictable and performant:
+Because async function components can be rerendered while they are still pending, Crank implements a couple rules to make concurrent updates predictable and performant:
1. There can only be one pending run of an element at the same time for the same tag and position. If the same async component is rerendered concurrently while it is still pending, another call is enqueued with the latest props.
@@ -51,7 +51,7 @@ async function Delay ({message}) {
})();
```
-In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings happen synchronously, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then only the fourth rendering is enqueued because third rendering’s props are obsolete by the time the component is ready to rerender. This behavior allows async components to always be kept up-to-date without producing excess calls to your async functions.
+In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings happen synchronously, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then, only the fourth rendering is enqueued because third rendering’s props are obsolete by the time the component is ready to update again. This behavior allows async components to always be kept up-to-date without producing excess calls to async functions.
2. If two different async components are rendered in the same position, the components are raced. If the earlier component fulfills first, it shows until the later component fulfills. If the later component fulfills first, the earlier component is never rendered. This ratcheting effect becomes useful for rendering fallback states for async components, as we’ll see later.
@@ -96,7 +96,7 @@ When Crank encounters an async component anywhere in the element tree, the entir
-->
## Async generator components
-Just as you can write stateful components with sync generator functions, you can also write stateful async components with async generator functions.
+Just as you can write stateful components with sync generator functions, you can also write stateful *async* components with *async generator functions*.
```jsx
async function *AsyncLabeledCounter ({message}) {
@@ -129,7 +129,7 @@ async function *AsyncLabeledCounter ({message}) {
})();
```
-`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in the section on sync generator components, and demonstrates several key differences between sync and async generator components. First, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because Crank contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Second, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed; Rather than suspending at each yield, we rely on the `for await…of` loop, which pauses at the bottom for the next rendering.
+`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in [the section on sync generator components](#TK). This example demonstrates several key differences between sync and async generator components. Firstly, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because Crank contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Secondly, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed; Rather than suspending at each yield, we rely on the `for await…of` loop, which suspends at the bottom of its block until the next update.
## Responsive Loading Indicators
The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes longer than expected. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described previously along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
@@ -185,9 +185,9 @@ function *RandomDogApp() {
renderer.render(, document.body);
```
-In this example, the `RandomDogLoader` component is an async generator component which races the `LoadingIndicator` component with the `RandomDog` component. Because the async generator component resumes continuously, both components are executed, and according to the second rule, only the second component shows if it fulfills faster than the first component, which fulfills at a fixed interval of one second.
+In this example, the `RandomDogLoader` component is an async generator component which races the `LoadingIndicator` component with the `RandomDog` component. Because the async generator component resumes continuously, both components are rendered, and according to the second rule of async components, the loading indicator only shows if `RandomDog` component takes longer than a second to fulfill.
-The preceding example hints at how we could abstract this pattern to implement a `Suspense` component, a proposed custom API in React which allows async components with fallback states:
+The preceding example hints at how we could abstract this pattern to implement a `Suspense` component, an API in React which allows for async components with fallback states:
```jsx
async function Fallback({timeout = 1000, children}) {
@@ -212,4 +212,4 @@ async function *Suspense({timeout, fallback, children}) {
})();
```
-As you can see, with Crank, no special tags are needed for async loading states, and the functionality to write this complex logic is implemented using the same element diffing algorithm that governs synchronous components. This approach is also more flexible in the sense that you can extend it, for instance, to include a second fallback state which fulfills after ten seconds, which might inform the user that something went wrong or that servers are slow to respond. Best of all, you can use async/await directly in your components!
+As you can see, no special tags are needed for async loading states, and the functionality to write this complex logic is implemented using the same element diffing algorithm that governs synchronous components. Additionally, this approach is more flexible in the sense that you can extend it, for instance, to include a second fallback state which fulfills after ten seconds, which might inform the user that something went wrong or that servers are slow to respond.
diff --git a/website/guides/06-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
index bb833f41c..112e6a5c9 100644
--- a/website/guides/06-special-props-and-tags.md
+++ b/website/guides/06-special-props-and-tags.md
@@ -2,12 +2,11 @@
title: Special Props and Tags
---
-The element diffing algorithm used by Crank is both declarative and efficient, but there are times when you might want to tweak the way it works. Crank provides special props and tags which produce different rendering behaviors.
+## Special Props
-## Special Crank Props
-### children
+The following are props which can be used with all elements, regardless of tag or renderer.
-### crank-key
+### `crank-key`
By default, Crank will use an element’s tag and position to determine if it represents an update or a change to the tree. Because elements often represent stateful DOM nodes or components, it can be useful to *key* the children of an element to hint to renderers that an element has been added, moved or removed. In Crank, we do this with the special prop `crank-key`:
```jsx
@@ -74,20 +73,65 @@ console.log(document.firstChild.firstChild === span); // true
```
### crank-ref
-TKTKTKTKTKTKTKTKTKTKTKTK
+Sometimes, you may want to access the rendered value of a specific element in the element tree. To do this, you can use the `crank-ref` prop, which takes a callback which is fired when the element is actually rendered.
+
+```tsx
+function *MyPlayer() {
+ let audio;
+ while (true) {
+ yield (
+
+
+
+ );
+ }
+}
+```
+
+Refs can be attached to any element in the element tree, and the value passed to the callback will vary according the type of the element and the renderer.
+
+### `children`
+The `children` prop passed to components is special because it is not usually set with JSX’s `key="value"` prop syntax, but by the contents between the opening and closing tags. Crank places no limitations on the types of values that can be passed into components as children, but patterns like [render props](https://reactjs.org/docs/render-props.html), where a callback is passed as the child of a component should be avoided.
+
+The actual type of the `children` prop will vary according to the number of children passed in. If a component element has no children (``), the `children` prop will be undefined, if it has one child (``), the `children` prop will be set to that child, and if it has multiple children (``), the `children` prop will be set to an array of those children. The reason we do this is to avoid an array allocation. All props have to be retained between renders, and avoiding allocating an extra array for every element in the tree can reduce the runtime memory costs of the framework dramatically.
+
+Therefore, the `children` prop should be treated as a black box, only to be rendered somewhere within a component’s returned or yielded children. Attempting to iterate over or manipulate the passed in children of a component is an anti-pattern, and you should use [event bubbling](#TKTKTKT) or [provisions](#TTKTKTK) to coordinate ancestor/descendant components.
## Special DOM Props
+
+The following props are specific to host elements for the HTML and DOM renderers.
+
### style
+The style prop can be used to add inline styles to an element. It can either be a CSS string, in which case it works exactly as it does in HTML, or it can also be an object, in which case CSS declarations can be set individually.
+
+```jsx
+
Hello
+```
+
+Unlike other JSX frameworks, Crank does not camel-case style properties or add pixel units to numbers.
### innerHTML
+The innerHTML prop can be used to set the `innerHTML` of an element.
-### onevent
+Be careful when using the `innerHTML` prop, as passing unsanitized text inputs can lead to security vulnerabilities. As an alternative, you can also use [the special `Raw` element tag](#TKTKTK), which allows to inject raw HTML or even actual DOM nodes into the element tree, without requiring a parent host element.
-### class/className
+### `class` vs `className`, `for` vs `htmlFor`
+Crank strives to make copying and pasting HTML into your components as easy as possible, and to this extent it allows you to use `class` and `for` as props in your elements.
+```jsx
+
+```
+
+You can still use `className` and `htmlFor` as well, but using the former prop names is much more convenient.
## Special Tags
-Crank provides several element tags which have special meaning when rendering. In actuality, these tags are symbols and behave similarly to string tags, except they affect the diffing algorithm and output.
+
+Crank provides four element tags which have special meaning to the renderer, and affect element diffing and rendering output in various ways.
### Fragment
Crank provides a `Fragment` tag, which allows you to render multiple children into a parent without wrapping them in another DOM node. Under the hood, iterables which appear in the element tree are also implicitly wrapped in a `Fragment` element by the renderer.
@@ -109,8 +153,23 @@ console.log(document.body.innerHTML);
// "
Sibling 1
Sibling 2
"
```
+Internally, the `Fragment` is the empty string, and you can use the empty string directly when calling `createElement` yourself without having to reference the `Crank` namespace.
+
+```jsx
+function Siblings() {
+ return createElement("", null, [
+
"
+```
+
### Portal
-Sometimes you may want to render into multiple DOM nodes from the same element tree. You can do this with the `Portal` tag, passing in a DOM node as its `root` prop. The Portal’s children will be rendered into the specified root. This is useful when writing modals or working with pages where you need to render into multiple entry-points. Events dispatched from a `Portal` element‘s child contexts via `this.dispatchEvent` will still bubble into parent component contexts.
+Sometimes you may want to render into a DOM node which isn’t the current parent element, or even a part of the currently rendered DOM tree. You can do this with the `Portal` tag, passing in a DOM node as its `root` prop. The Portal’s children will be rendered into the specified root element, just as if Renderer.render was called with the root value as its second argument.
```jsx
/** @jsx createElement */
@@ -136,6 +195,8 @@ console.log(root2.innerHTML);
// "
This div is rendered into root2
"
```
+This tag is useful for instance when creating modals or tooltips, which usually need to be rendered into separate DOM elements at the bottom of the page for visibility reasons. Events dispatched from a `Portal` element‘s child contexts via the `dispatchEvent` method will still bubble into parent components.
+
### Copy
It‘s often fine to rerender Crank components, because elements are diffed, persistent between renders, and unnecessary mutations usually avoided. However, you might want to prevent a child from updating when the parent rerenders, perhaps because a certain prop hasn’t changed, because you want to batch updates from the parent, or as a performance optimization. To do this, you can use the `Copy` tag to indicate to Crank that you don’t want to update a previously rendered element in its position.
From 1684a67c5af87d18a9196ffeebe4e47394c70584 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Wed, 15 Jul 2020 19:28:50 -0400
Subject: [PATCH 08/26] anchor headers
---
website/build.tsx | 14 ++++++++++++++
website/src/index.css | 25 ++++++++++++++++++++-----
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/website/build.tsx b/website/build.tsx
index e37586123..fdf672588 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -41,6 +41,20 @@ interface DocInfo {
publishDate?: Date;
}
+const markedRenderer: Partial = {
+ heading(text, level, raw, slugger) {
+ const slug = slugger.slug(raw);
+ if (level <= 3) {
+ return `
+ ${text}
+ `;
+ }
+ return `${text}`;
+ },
+};
+
+marked.use({renderer: markedRenderer as marked.Renderer});
+
async function parseDocs(
name: string,
root: string = name,
diff --git a/website/src/index.css b/website/src/index.css
index a1c975e37..2c34cea31 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -25,12 +25,17 @@ img {
h1, h2, h3, h4, h5, h6 {
padding: 0;
margin: 1.4em 0;
+ a {
+ color: inherit;
+ text-decoration: none;
+ }
}
h1, h2, h3 {
color: #dbb368;
}
+
blockquote {
border-left: 2px solid #dbb368;
margin: 1.5em 0;
@@ -43,12 +48,22 @@ blockquote {
a {
color: inherit;
-}
+ &.current {
+ color: #dbb368;
+ }
-a.current {
- color: #dbb368;
+ &.anchor {
+ outline: none;
+ &::before {
+ content: "";
+ display: inline-block;
+ margin-top: -5em;
+ padding-top: 5em;
+ }
+ }
}
+
li {
margin: 0 0 1em;
}
@@ -172,11 +187,11 @@ li {
.content {
margin: 0 auto;
padding: 2rem 1rem;
- :first-child {
+ > :first-child {
margin-top: 0;
}
- :last-child {
+ > :last-child {
margin-bottom: 0;
}
From be2ffcf949d093a657527432c7328b6ad599415a Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Wed, 15 Jul 2020 23:39:05 -0400
Subject: [PATCH 09/26] more docs
---
website/guides/01-getting-started.md | 2 +-
website/guides/02-elements.md | 33 +++---
website/guides/03-components.md | 43 +++++---
website/guides/04-handling-events.md | 113 ++++++++++++++++++--
website/guides/06-special-props-and-tags.md | 16 +--
website/guides/12-api-reference.md | 24 ++---
6 files changed, 172 insertions(+), 59 deletions(-)
diff --git a/website/guides/01-getting-started.md b/website/guides/01-getting-started.md
index 6d8f30c54..583e8c464 100644
--- a/website/guides/01-getting-started.md
+++ b/website/guides/01-getting-started.md
@@ -17,7 +17,7 @@ import {renderer} from "@bikeshaving/crank/dom";
renderer.render(
Hello world
, document.body);
```
-If your environment does not support ESModules (you’ll probably see a message like `SyntaxError: Unexpected token export`), you can import the CommonJS versions of the library under the `cjs` directory.
+If your environment does not support ESModules (you may see a message like `SyntaxError: Unexpected token export`), you can import the CommonJS versions of the library under the `cjs` directory.
```jsx
/** @jsx createElement */
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index 72552226f..20956fbb2 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -3,9 +3,11 @@ id: elements
title: JSX, Elements and Renderers
---
-**Note for React developers:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to the section on components. Elements in Crank work almost exactly as they do in React.
+**Note:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to [the guide for components](./components). Elements in Crank work almost exactly as they do in React.
-Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box; all you need to do is enable JSX parsing, import the `createElement` function, and include a `@jsx` comment directive (`/** @jsx createElement */`). The parser will then transpile JSX into `createElement` calls. For example, in the following code, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
+## Elements
+
+Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box. JSX transpilers work by transforming JSX expressions into `createElement` factory function calls. For example, in the following code, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
```jsx
/** @jsx createElement */
@@ -16,7 +18,9 @@ const el =
An element
;
const el1 = createElement("div", {id: "element"}, "An element");
```
-The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, Crank provides special classes called *renderers* which interpret elements to produce and manage DOM nodes, HTML strings, canvas scene graphs, or whatever else you can think of. Crank ships with two renderers for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
+The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, Crank provides special classes called *renderers* which interpret elements to produce DOM nodes, HTML strings, or whatever else you can think of.
+
+Crank ships with two renderer subclasses for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
```jsx
/** @jsx createElement */
@@ -31,14 +35,14 @@ console.log(node.innerHTML); //
Hello world
console.log(HTMLRenderer.render(el)); //
Hello world
```
-## The parts of an element
+## The Parts of an Element

-An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed contents and mostly still work. The advantage of using JSX is that it allows you to interpolate arbitrary JavaScript expressions as an element’s tag, props or children.
+An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to the syntax for tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JSX-flavored JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can contain unbalanced or malformed and mostly still work. The advantage of using JSX is that it allows you to interpolate arbitrary JavaScript expressions as an element’s tag, props or children.
### Tags
-Tags are the first part of an element expression, and can be thought of as the “name” or “type” of the element. JSX parsers will transpile the tag name as the first argument of a `createElement` call.
+Tags are the first part of a JSX element expression, and can be thought of as the “name” or “type” of the element. JSX parsers will transpile the tag name as the first argument of a `createElement` call.
```jsx
const intrinsicEl = ;
@@ -50,11 +54,10 @@ const componentEl = ;
const componentEl1 = createElement(Component, null);
```
-By convention, JSX parsers treat lower-cased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. We call elements with string tags *host* or *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined by the execution of the referenced function and not the renderer. Elements with function tags are called *component elements*.
-
+By convention, JSX parsers treat lowercased tags as strings and capitalized tags as variables. When a tag is a string, this signifies that the element will be handled by the renderer. We call elements with string tags *host* or *intrinsic* elements, and for both of the web renderers, these elements correspond to actual HTML elements like `div` or `input`. As we’ll see later, elements can also have function tags, in which case the behavior of the element is defined not by the renderer but by the execution of the referenced function. Elements with function tags are called *component elements*.
### Props
-JSX parsers coalesce the attribute-like `key="value"` syntax to a single object for each element, and pass this object as the second argument to the resulting `createElement` call.
+JSX parsers coalesce the attribute-like `key="value"` syntax to a single object for each element, and pass this object to the resulting `createElement` call as its second argument.
```jsx
const myClass = "my-class";
@@ -65,7 +68,7 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```
-We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript value if the value is placed within curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you might pass arguments into functions when invoking them.
+We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you might pass arguments into functions when invoking them.
If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).
@@ -77,7 +80,7 @@ const el1 = createElement("img", {...props, id: "2"});
```
### Children
-As with HTML, Crank elements can have contents, placed between its opening and closing tags. These contents are referred to as *children*. Because elements can have children which are also elements, they form a nested tree of nodes which we call the *element tree*.
+As with HTML, Crank elements can have contents, placed between its opening and closing tags. These contents are referred to as the element’s *children.* Because elements can have children which are also elements, they form a tree of nodes which we call the *element tree*.
```jsx
const list = (
@@ -95,7 +98,7 @@ const list1 = createElement("ul", null,
console.log(list.props.children.length); // 2
```
-As you can see in the example, JSX parsers interpret the contents of an element which fall outside elements as strings. However, just as with props, you can use curly brackets to interpolate JavaScript expressions into an element’s children. Besides elements and strings, almost every value in JavaScript can participate in an element tree. Numbers are rendered as strings, and the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
+As seen in the example, JSX parsers interpret the contents of elements which are not themselves elements as strings. However, just as with props, you can use curly brackets to interpolate JavaScript expressions into an element’s children. Besides elements and strings, almost every value in JavaScript can participate in an element tree. Numbers are rendered as strings, and the values `null`, `undefined`, `true` and `false` are erased, allowing you to render things conditionally with boolean expressions.
```jsx
const el =
"
```
-## Element diffing
-Crank uses the same “virtual DOM diffing” algorithm made popular by React, where we compare elements by tag and position to reduce and reuse DOM mutations. This approach allows you to write declarative code which focuses on producing the right element tree, while Crank does the dirty work of managing state and mutating the DOM.
+## Element Diffing
+Crank uses the same “virtual DOM” diffing algorithm made popular by React, where we compare elements by tag and position to reduce the number of DOM mutations and reuse DOM nodes. This approach allows you to write declarative code which focuses on producing the right tree, while Crank does the dirty work of managing state and mutating the DOM.
```jsx
renderer.render(
@@ -138,4 +141,4 @@ console.log(document.body.firstChild === div); // true
console.log(document.body.firstChild.firstChild === span); // true
```
-**NOTE:** We usually avoid using the term “virtual DOM” in Crank, insofar as the Crank renderer can render to multiple environments; instead, we use the term “element diffing” to mean mostly the same thing.
+**Note:** We usually avoid using the term “virtual DOM” in Crank, insofar as the core renderer can be extended to target multiple environments; instead, we use the term “element diffing” to mean mostly the same thing.
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index 3fec69046..6a79c0823 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -5,7 +5,7 @@ title: Components
So far, we’ve only seen and used host elements, but eventually, we’ll want to group parts of the element tree into reusable *components.* In Crank, all components are functions; there is no class-based component API.
## Basic Components
-The simplest kind of component you can write is a *sync function component*. When rendered, the function is invoked with the props object of the element as its first argument, and the return value of the function is recursively rendered as the element’s children.
+The simplest kind of component is a *function component*. When rendered, the function is invoked with the props of the element as its first argument, and the return value of the function is recursively rendered as the element’s children.
```js
function Greeting({name}) {
@@ -37,18 +37,8 @@ renderer.render(
console.log(document.body.innerHTML); // "
Message for Nemo: Howdy
"
```
-You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to a specific prop by using JavaScript’s default value syntax.
-
-```js
-function Greeting({name="World"}) {
- return
Hello, {name}
;
-}
-
-renderer.render(, document.body); // "
Hello World
"
-```
-
## Stateful Components
-Eventually, you’re going to want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so. These types of components are referred to as *sync generator components*.
+Eventually, youre going to want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so. These types of components are referred to as *generator components*.
```jsx
function *Counter() {
@@ -82,10 +72,10 @@ console.log(document.body.innerHTML);
// "
You have updated this component 1 time
"
```
-Because we’re now yielding elements rather than returning them, we can make components stateful using variables in the local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered, just as if it were returned in a sync function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of the generator is preserved between renders. This allows local state to be encapsulated within the generator’s scope.
+By yielding elements rather than returning them, we can make components stateful using variables in the generator’s local scope. Every time the component is updated, Crank resumes the generator, pausing at the next `yield`. The yielded expressions, usually elements, are then recursively rendered, just as if they were returned from a function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of generator components are preserved between renders.
### Contexts
-In the preceding example, the `Counter` component’s local state only changes when it is rerendered, but we may want to write components which update themselves based on timers or events. Crank allows components to control themselves by passing in a custom object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component in place.
+In the preceding example, the `Counter` component’s local state changes when it is rerendered, but we may want to write components which update themselves instead according to timers or events. Crank allows components to control themselves by passing in an object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component instance in place.
```jsx
function *Timer() {
@@ -111,7 +101,7 @@ This `Timer` component is similar to the `Counter` one, except now the state (th
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval`. Crank will call the `return` method on generator components when the element is removed from the tree, so that the finally block executes and `clearInterval` is called. In this way, you can use the natural lifecycle of a generator to write setup and teardown logic for components, all within the same scope.
-### Prop updates
+### Props Updates
The generator components we’ve seen so far haven’t used props. Generator components can accept props as its first parameter just like regular function components.
```jsx
@@ -183,3 +173,26 @@ console.log(document.body.innerHTML); // "
Hello again Bob
"
```
The fact that state is just local variables allows us to blur the lines between props and state, in a way that is easy to understand and without lifecycle methods like `componentWillUpdate` from React. With generators and `for` loops, comparing old and new props is as easy as comparing adjacent elements of an array.
+
+## Default Props
+You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to a specific prop by using JavaScript’s default value syntax.
+
+```jsx
+function Greeting({name="World"}) {
+ return
Hello, {name}
;
+}
+
+renderer.render(, document.body); // "
Hello World
"
+```
+
+This works well for function components, but for generator components, you should make sure that you use the same default in both the parameter list and the `for` statement.
+
+```jsx
+function *Greeting({name="World"}) {
+ for ({name="World"} of this) {
+ yield
Hello, {name}
;
+ }
+}
+```
+
+Components which mismatch default props between these two positions can cause surprising behavior.
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
index b6bd0876f..f74d5b43c 100644
--- a/website/guides/04-handling-events.md
+++ b/website/guides/04-handling-events.md
@@ -2,10 +2,10 @@
title: Handling Events
---
-Most DOM applications require some measure of interactivity, where the user interface updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
+Most web applications require some measure of interactivity, where the user interface updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
-## onevent props
-You can attach event callbacks to host element directly using onevent props. These props start with `on`, are all lowercase, and correspond exactly to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
+## DOM `onevent` Props
+You can attach event callbacks to host element directly using `onevent` props. These props start with `on`, are all lowercase, and correspond to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
```jsx
function *Clicker() {
@@ -26,7 +26,7 @@ function *Clicker() {
}
```
-## The EventTarget interface
+## The EventTarget Interface
As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM.
```jsx
@@ -49,7 +49,7 @@ The local state `count` is now updated in the event listener, which triggers whe
**NOTE:** When using the `addEventListener` method, you do not have to call the `removeEventListener` method if you merely need to remove event listeners when the component is unmounted. This is done automatically.
-## Event delegation
+## Event Delegation
The Context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
@@ -76,10 +76,10 @@ function *Clicker() {
Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button` element.
-## When to use onevent props and the EventTarget API
-The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and you can register them on the exact element you’d like to listen to.
+## `onevent` vs `EventTarget`
+The props-based `onevent` API and the context-based `EventTarget` API both have their advantages. On the one hand, using `onevent` props means you don’t have to filter events by target, and you can register them on the exact element you’d like to listen to.
-On the other hand, using `this.addEventListener` allows you to take full advantage of the EventTarget API, including registering passive event listeners or events which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to the produced elements.
+On the other, using the `addEventListener` method allows you to take full advantage of the EventTarget API, including registering passive event listeners or events which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to produced elements.
Crank supports both API styles for convenience and flexibility.
@@ -127,3 +127,100 @@ function *MyApp() {
```
`MyButton` is a function component which wraps a `button` element. It dispatches a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the id of the clicked button. This event is not triggered or bubbled on the underlying DOM nodes; instead, it can be listened for by parent component contexts using event bubbling, and in the example, the event propagates and is handled by the `MyApp` component. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
+
+## Controlled and Uncontrolled Props
+Form elements like ``, `
+
);
}
@@ -384,13 +384,13 @@ function GuidePage({title, html, docs, url}: GuidePageProps): Element {
return (
-
+
{title}
-
+
);
}
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index ad56044e9..0b1bb8ff3 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -3,11 +3,11 @@ id: elements
title: JSX, Elements and Renderers
---
-**Note:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to [the guide for components](./components). Elements in Crank work almost exactly as they do in React.
+**Note:** If you’re familiar with how JSX and elements work in React, you may want to skip ahead to [the guide on components](./components). Elements in Crank work almost exactly as they do in React.
-## Elements
+## JSX
-Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box. JSX transpilers work by transforming JSX expressions into `createElement` factory function calls. For example, in the following code, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
+Crank is best used with [JSX](https://facebook.github.io/jsx/), an XML-like syntax extension to JavaScript. It is designed to work with transpilers like Babel and TypeScript out-of-box. JSX transpilers work by transforming JSX expressions into `createElement` function calls. For example, in the following code, the JSX expression assigned to `el` transpiles to the `createElement` call assigned to `el1`.
```jsx
/** @jsx createElement */
@@ -39,10 +39,10 @@ console.log(HTMLRenderer.render(el)); //
Hello world

-An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to the syntax for tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JSX-flavored JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can be unbalanced or malformed and mostly still work. The advantage of using JSX is that it allows you to interpolate arbitrary JavaScript expressions as an element’s tag, props or children.
+An element can be thought of as having three main parts: a *tag*, *props* and *children*. These roughly correspond to the syntax for tags, attributes and content in HTML, and for the most part, you can copy-paste HTML into JSX-flavored JavaScript and have things work as you would expect. The main difference is that JSX has to be well-balanced like XML, so void tags must have a closing slash (`` not ``). Also, if you forget to close an element or mismatch opening and closing tags, the parser will throw an error, whereas HTML can be unbalanced or malformed and mostly still work. The advantage of using JSX is that it allows you to interpolate arbitrary JavaScript expressions as an element’s tag, props or children.
### Tags
-Tags are the first part of a JSX element expression, and can be thought of as the “name” or “type” of the element. JSX parsers will transpile the tag name as the first argument of a `createElement` call.
+Tags are the first part of a JSX element expression, and can be thought of as the “name” or “type” of the element. JSX transpilers pass the tag of an element to the resulting `createElement` call as its first argument.
```jsx
const intrinsicEl = ;
@@ -68,7 +68,7 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```
-We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you might pass arguments into functions when invoking them.
+We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how we “pass” arguments into functions when invoking them.
If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).
@@ -107,7 +107,7 @@ renderer.render(el, document.body);
console.log(document.body.innerHTML); //
a2
```
-Crank also allows arbitrarily nested iterables of values to be inserted, so, for instance, you can interpolate an array or a set of values into an element tree.
+Crank also allows arbitrarily nested iterables of values to be interpolated as children, so, for instance, you can insert an array or a set of values into element trees.
```jsx
const arr = [1, 2, 3];
@@ -117,7 +117,7 @@ console.log(document.body.innerHTML); // "
123 abc
"
```
## Element Diffing
-Crank uses the same “virtual DOM” diffing algorithm made popular by React, where we compare elements by tag and position to reduce DOM mutations and reuse nodes. This approach allows us to write declarative code which focuses on producing the right tree, while Crank does the dirty work of managing state and mutating the DOM.
+Crank uses the same “virtual DOM” diffing algorithm made popular by React, where we compare elements by tag and position to reduce DOM mutations and reuse nodes. This approach allows you to write declarative code which focuses on producing the right tree, while the framework does the dirty work of managing state and mutating the DOM.
```jsx
renderer.render(
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index 646516ada..a5afb9eaa 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -38,7 +38,7 @@ console.log(document.body.innerHTML); // "
"
```
@@ -195,4 +199,4 @@ function *Greeting({name="World"}) {
}
```
-A mismatch in default values for a prop between these two positions may cause surprising behavior.
+A mismatch in the default values for a prop between these two positions may cause surprising behavior.
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
index 29d17c0b1..7d76c3151 100644
--- a/website/guides/04-handling-events.md
+++ b/website/guides/04-handling-events.md
@@ -4,8 +4,8 @@ title: Handling Events
Most web applications require some measure of interactivity, where the user interface updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
-## DOM `onevent` Props
-You can attach event callbacks to host element directly using `onevent` props. These props start with `on`, are all lowercase, and correspond to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
+## DOM onevent Props
+You can attach event callbacks to host element directly using onevent props. These props start with `on`, are all lowercase, and correspond to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
```jsx
function *Clicker() {
@@ -27,7 +27,7 @@ function *Clicker() {
```
## The EventTarget Interface
-As an alternative to the `onevent` API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM.
+As an alternative to the onevent props API, Crank contexts also implement the same `EventTarget` interface used by the DOM. The `addEventListener` method attaches a listener to a component’s rendered DOM node or nodes.
```jsx
function *Clicker() {
@@ -47,11 +47,11 @@ function *Clicker() {
The local state `count` is now updated in the event listener, which triggers when the rendered button is actually clicked.
-**NOTE:** When using the `addEventListener` method, you do not have to call the `removeEventListener` method if you merely want to remove event listeners when the component is unmounted. This is done automatically.
+**NOTE:** When using the context’s `addEventListener` method, you do not have to call the `removeEventListener` method if you merely want to remove event listeners when the component is unmounted. This is done automatically.
## Event Delegation
-The Context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
+The context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
```jsx
function *Clicker() {
@@ -76,15 +76,15 @@ function *Clicker() {
Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button` element.
-## `onevent` vs `EventTarget`
-The props-based `onevent` API and the context-based `EventTarget` API both have their advantages. On the one hand, using `onevent` props means you don’t have to filter events by target, and you can register them on exactly the element you’d like to listen to.
+## onevent vs EventTarget
+The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and you can register them on exactly the element you’d like to listen to.
On the other, using the `addEventListener` method allows you to take full advantage of the EventTarget API, including registering passive event listeners or events which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to produced elements.
Crank supports both API styles for convenience and flexibility.
## Dispatching events
-Crank contexts implement the full EventTarget interface, meaning you can use the `dispatchEvent` method and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) constructor to dispatch events to ancestor components:
+Crank contexts implement the full EventTarget interface, meaning you can use the `dispatchEvent` method and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) class to dispatch events to ancestor components:
```jsx
function MyButton(props) {
@@ -126,14 +126,14 @@ function *MyApp() {
}
```
-`MyButton` is a function component which wraps a `button` element. It dispatches a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the ID of the clicked button. This event is not triggered or bubbled on the underlying DOM nodes; instead, it can be listened for by parent component contexts using event bubbling, and in the example, the event propagates and is handled by the `MyApp` component. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
+`MyButton` is a function component which wraps a `button` element. It dispatches a `CustomEvent` whose type is `"mybuttonclick"` when it is pressed, and whose `detail` property contains data about the ID of the clicked button. This event is not triggered on the underlying DOM nodes; instead, it can be listened for by parent component contexts using event bubbling, and in the example, the event propagates and is handled by the `MyApp` component. Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions used in other frameworks like Redux or VueX.
The preceding example also demonstrates a slight difference in the way the `addEventListener` method works in function components compared to generator components. With generator components, listeners stick between renders, and will continue to fire until the component is unmounted. However, with function components, because the `addEventListener` call would be invoked every time the component is rerendered, we remove and add listeners for every render. This allows function components to remain stateless while still listening for and dispatching events to their parents.
## Controlled and Uncontrolled Props
Form elements like inputs and textareas are stateful and by default update themselves automatically according to user input. JSX libraries like React and Inferno handle these types of elements by allowing their virtual representations to be “controlled” or “uncontrolled,” where being controlled means that the internal DOM node’s state is synced to the virtual representation’s props.
-Crank’s philosophy with regard to this issue is slightly different, in that we do not view the virtual elements as the “source of truth” for the underlying DOM node. In practice, this design decision means that renderers do not retain the previously rendered props for host elements. For instance, Crank will not compare old and new props between renders to avoid mutating props which have not changed, and instead attempt to update every prop specified in an element’s props when it is rerendered.
+Crank’s philosophy with regard to this issue is slightly different, in that we do not view the virtual elements as the “source of truth” for the underlying DOM node. In practice, this design decision means that renderers do not retain the previously rendered props for host elements. For instance, Crank will not compare old and new props between renders to avoid mutating props which have not changed, and instead attempt to update every prop in props when it is rerendered.
Another consequence is that we don’t delete props which were present in one rendering and absent in the next. For instance, in the following example, the checkbox will not be unchecked if you press the button.
@@ -219,4 +219,4 @@ function* ResettingInput() {
}
```
-We use the `reset` flag to check whether we need to set the `value` of the underlying input DOM element, and we omit the `value` prop when we aren’t performing a reset. This means that for the most part, we can let the browser deal with the input element without rerendering the component.
+We use the `reset` flag to check whether we need to set the `value` of the underlying input DOM element, and we omit the `value` prop when we aren’t performing a reset. This design decision means we can let the browser deal with form elements without rerendering or managing state, while still allowing them to be rerendered as necessary.
diff --git a/website/guides/05-async-components.md b/website/guides/05-async-components.md
index 16c99e4cc..7eff0030f 100644
--- a/website/guides/05-async-components.md
+++ b/website/guides/05-async-components.md
@@ -3,7 +3,7 @@ title: Async Components
---
## Async function components
-So far, every component we’ve seen has worked synchronously, and Crank will respect this as an intentional decision by keeping the entire process of rendering synchronous from start to finish. However, modern JavaScript includes promises and `async`/`await`, which allows you to write concurrently executing code as if it were synchronous. To facilitate this style of code, Crank allows components to be asynchronous functions as well.
+So far, every component we’ve seen has worked synchronously, and Crank will respect this as an intentional decision by the developer by keeping the entire process of rendering synchronous from start to finish. However, modern JavaScript includes promises and `async`/`await`, which allow you to write concurrently executing code as if it were synchronous. To facilitate this style of code, Crank allows components to be asynchronous functions as well.
```jsx
async function IPAddress () {
@@ -18,7 +18,7 @@ async function IPAddress () {
})();
```
-When a Crank renderer runs a component which returns a promise, the rendering process becomes asynchronous as well. Concretely, this means that `renderer.render` call will return a promise which fulfills when all async components in the element tree have fulfilled at least once, and that no actual DOM updates will be triggered until this happens.
+When a Crank renderer runs a component which returns a promise, the rendering process becomes asynchronous as well. Concretely, this means that `renderer.render` calls with async components in the tree will return a promise which fulfills when all async components in the element tree have fulfilled, and no actual DOM updates will be triggered until this happens.
### Concurrent updates
Because async function components can be rerendered while they are still pending, Crank implements a couple rules to make concurrent updates predictable and performant:
@@ -51,9 +51,9 @@ async function Delay ({message}) {
})();
```
-In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings happen synchronously, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then, only the fourth rendering is enqueued because third rendering’s props are obsolete by the time the component is ready to update again. This behavior allows async components to always be kept up-to-date without producing excess calls to async functions.
+In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings are enqueued, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then, only the fourth rendering is actually executed because third rendering’s props are obsolete by the time the component is ready to update again. This behavior allows async components to always be kept up-to-date without producing excess calls to async functions.
-2. If two different async components are rendered in the same position, the components are raced. If the earlier component fulfills first, it shows until the later component fulfills. If the later component fulfills first, the earlier component is never rendered. This ratcheting effect becomes useful for rendering fallback states for async components, as we’ll see later.
+2. If two different async components are rendered in the same position, the components are raced. If the earlier component fulfills first, it shows until the later component fulfills. If the later component fulfills first, the earlier component is never rendered.
```jsx
async function Fast() {
@@ -89,6 +89,8 @@ async function Slow() {
})();
```
+As we’ll see later, this ratcheting effect becomes useful for rendering fallback states for async components.
+
@@ -131,10 +131,10 @@ async function *AsyncLabeledCounter ({message}) {
})();
```
-`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in [the section on props updates](./components#props-updates). This example demonstrates several key differences between sync and async generator components. Firstly, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because Crank contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Secondly, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed. Rather than suspending at each yield, we rely on the `for await…of` loop, which suspends at the bottom of the loop until the next update.
+`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in [the section on props updates](./components#props-updates). This example demonstrates several key differences between sync and async generator components. Firstly, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Secondly, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators components to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed. Rather than suspending at each yield, we rely on the `for await…of` loop, which suspends at the bottom until the next update.
## Responsive Loading Indicators
-The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes longer than expected. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described previously along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
+The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes a long time. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described previously along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
```jsx
async function LoadingIndicator() {
@@ -187,7 +187,7 @@ function *RandomDogApp() {
renderer.render(, document.body);
```
-In this example, the `RandomDogLoader` component is an async generator component which races the `LoadingIndicator` component with the `RandomDog` component. Because the async generator component resumes continuously, both components are rendered, and according to the second rule of async components, the loading indicator only shows if the `RandomDog` component takes longer than a second to fulfill.
+In this example, the `RandomDogLoader` component is an async generator component which races the `LoadingIndicator` component with the `RandomDog` component. Because the async generator component resumes continuously, both components are rendered, and according to the second rule of async components, the loading indicator only shows if the `RandomDog` component takes longer than the `LoadingIndicator` component, which fulfills at a fixed interval of one second.
The preceding example hints at how we could abstract this pattern to implement the `Suspense` component from React.
diff --git a/website/guides/06-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
index 31c85067b..f7596b06f 100644
--- a/website/guides/06-special-props-and-tags.md
+++ b/website/guides/06-special-props-and-tags.md
@@ -2,12 +2,13 @@
title: Special Props and Tags
---
-## Special Props
+Crank provides certain APIs in the form of special props or element tags.
+## Special Props
The following props apply to all elements, regardless of tag or renderer.
### `crank-key`
-By default, Crank will use an element’s tag and position to determine if it represents an update or a change to the tree. Because elements often represent stateful DOM nodes or components, it can be useful to *key* the children of an element to hint to the renderer that an element has been added, moved or removed. In Crank, we do this with the special prop `crank-key`:
+By default, Crank uses an element’s tag and position to determine if it represents an update or a change to the tree. Because elements often represent stateful DOM nodes or components, it can be useful to *key* the children of an element to hint to the renderer that an element has been added, moved or removed from a parent. In Crank, we do this with the special prop `crank-key`:
```jsx
let nextId = 0;
@@ -42,7 +43,7 @@ console.log(document.body.innerHTML);
// "
Id: 3Id: 2Id: 1
"
```
-Keys are scoped to an element’s children, and can be any JavaScript value. When rendering iterables, it’s useful to key elements of the iterable, because it’s common for the contents of rendered iterables to added, moved or removed. All elements in the element tree can be keyed.
+Keys are scoped to an element’s children, and can be any JavaScript value. When rendering iterables, it’s useful to key elements of the iterable, because it’s common for the values of rendered iterables to added, moved or removed.
```jsx
function *Shuffler() {
@@ -72,6 +73,8 @@ console.log(document.body.innerHTML);
console.log(document.firstChild.firstChild === span); // true
```
+All elements in the element tree can be keyed. If the element is a component element, the `crank-key` prop is erased from the props object passed to the component.
+
### `crank-ref`
Sometimes, you may want to access the rendered value of a specific element in the element tree. To do this, you can pass a callback as the `crank-ref` prop. This callback is called with the rendered value of the element when the element has committed.
@@ -93,12 +96,12 @@ function *MyPlayer() {
}
```
-Refs can be attached to any element in the element tree, and the value passed to the callback will vary according the type of the element and the renderer.
+Refs can be attached to any element in the element tree, and the value passed to the callback will vary according the type of the element and the specific renderer.
### `children`
The `children` prop passed to components is special because it is not usually set with JSX’s `key="value"` prop syntax, but by the contents between the opening and closing tags. Crank places no limitations on the types of values that can be passed into components as children, but patterns like [render props](https://reactjs.org/docs/render-props.html) from the React community, where a callback is passed as the child of a component, should be avoided.
-The actual type of the `children` prop will vary according to the number of children passed in. If a component element has no children (``), the `children` prop will be undefined, if it has one child (``), the `children` prop will be set to that child, and if it has multiple children (``), the `children` prop will be set to an array of those children. We do this to avoid an extra array allocation; all props have to be retained between renders, and most elements contain only zero or one children, so avoiding the allocation of an extra array for every element in the tree can noticeably reduce runtime memory costs.
+The actual type of the `children` prop will vary according to the number of children passed in. If a component element has no children (``), the `children` prop will be undefined, if it has one child (``), the `children` prop will be set to that child, and if it has multiple children (``), the `children` prop will be set to an array of those children. We do this to reduce runtime memory costs. All props have to be retained between renders, and most elements contain only zero or one child, so avoiding the allocation of an extra array for every element in the tree can noticeably reduce memory requirements.
Therefore, the `children` prop should be treated as a black box, only to be rendered somewhere within a component’s returned or yielded children. Attempting to iterate over or manipulate the passed in children of a component is an anti-pattern, and you should use [event dispatch](./handling-events#dispatching-events) or [provisions](./reusable-logic#provisions) to coordinate ancestor and descendant components.
@@ -122,14 +125,14 @@ Be careful when using the `innerHTML` prop, as passing unsanitized text inputs c
As an alternative, you can also use [the special `Raw` element tag](#raw), which allows to inject raw HTML or even actual DOM nodes into the element tree, without requiring a parent host element.
-### `class` and `for`
+### Prop Naming Conventions
Crank strives to make copying and pasting HTML into your components as easy as possible, and to this extent it allows you to use `class` and `for` as props in your elements instead of `className` and `htmlFor`.
```jsx
```
-You can still use the `className` and `htmlFor` props as well, but using the former names is preferred.
+You can still use the `className` and `htmlFor` props as well, but using the former names is preferred. This philosophy also extends to SVG elements, and you can use props like `clip-path` and `stroke-width` without having to camel case them.
## Special Tags
@@ -214,7 +217,7 @@ console.log(root2.innerHTML);
// "
This div is rendered into root2
"
```
-This tag is useful for instance when creating modals or tooltips, which usually need to be rendered into separate DOM elements at the bottom of the page for visibility reasons. Events dispatched from a `Portal` element‘s child components via the `dispatchEvent` method will still bubble into parent components.
+This tag is useful for creating modals or tooltips, which usually need to be rendered into separate DOM elements at the bottom of the page for visibility reasons. Events dispatched from a `Portal` element‘s child components via the `dispatchEvent` method will still bubble into parent components.
### Raw
Sometimes, you may want to insert raw HTML or actual DOM nodes directly into the element tree. Crank allows you to do this with the `Raw` element. The `Raw` element takes a `value` prop which is interpreted by the renderer. For the DOM renderer, if `value` is an HTML string, the renderer will parse and insert the resulting DOM nodes. If the value is already a DOM node, Crank will insert them in place.
diff --git a/website/guides/07-lifecycles.md b/website/guides/07-lifecycles.md
index bd72f2d62..e0725ae83 100644
--- a/website/guides/07-lifecycles.md
+++ b/website/guides/07-lifecycles.md
@@ -2,9 +2,9 @@
title: Lifecycles
---
-Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the generator object as components are mounted, updated and unmounted from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
+Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the returned generator object as components are mounted, updated and unmounted from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
-## Returning from a generator
+## Returning
Usually, you’ll yield in generator components so that they can continue to respond to updates, but you may want to also `return` a final state. Unlike function components, which are called and returned once for each update, once a generator component returns, it will never update again.
@@ -46,9 +46,9 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // "1"
```
-## Cleaning up after your components are unmounted
+## Cleaning Up
-When a generator component is removed from the tree, Crank calls the `return` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator was suspended are broken out of, and code after the yield does not execute.
+When a generator component is removed from the tree, Crank calls the `return` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator suspended are broken out of, and code after the yield does not execute.
You can take advantage of this behavior by wrapping your `yield` loops in a `try`/`finally` block to release any resources that your component may have used.
@@ -70,10 +70,10 @@ renderer.render(null, document.body);
console.log(document.body); // ""
```
-[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to try/finally blocks in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the produced values and doing so might cause your components to inadvertently swallow errors or suspend in an unexpected location.
+[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to `try`/`finally` blocks in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the produced values and doing so might cause your components to inadvertently swallow errors or suspend in an unexpected location.
-## Catching errors thrown by children
-We all make mistakes, and it can be useful to catch errors in our components so that we can show the user something or notify error-logging services. To facilitate this, Crank will catch errors thrown when rendering child elements and throw them back into parent generator components by calling the `throw` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced with a `throw` statement with the error set to whatever was thrown by children.
+## Catching Errors
+We all make mistakes, and it can be useful to catch errors in our components so that we can show the user something or notify error-logging services. To facilitate this, Crank will catch errors thrown when rendering child elements and throw them back into parent generator components by calling the `throw` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced with a `throw` statement with the error set to whatever was thrown by the component’s children.
You can take advantage of this behavior by wrapping your `yield` operations in a `try`/`catch` block to catch errors caused by children.
@@ -126,16 +126,13 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // "
I’ll be back
"
```
-Note that you can’t catch or recover from errors thrown from within the generator themselves, the yield operator only throws errors which were thrown in the course of rendering child elements.
-
-## Accessing rendered values
-Sometimes, the declarative rendering of DOM nodes is not enough, and you’ll want to access the actual DOM nodes you’ve rendered, to make measurements or call imperative methods like the `focus` method for form elements, or the `play` method for media elements. To facilitate this, Crank will pass rendered DOM nodes back into the generator using the `next` method, so `yield` expressions can be read and assigned to access the actual rendered DOM nodes.
+## Accessing Rendered Values
+Sometimes, declarative rendering is not enough, and you’ll want to access the actual DOM nodes you’ve rendered, to make measurements or call imperative methods like the `focus` method for form elements, or the `play` method for media elements. To facilitate this, Crank will pass rendered DOM nodes back into the generator using the `next` method. This means that as a developer, you can read `yield` expressions to access the actual rendered DOM nodes.
```jsx
async function *FocusingInput(props) {
- let input;
for await (props of this) {
- input = yield ;
+ const input = yield ;
input.focus();
}
}
@@ -145,16 +142,15 @@ The `MyInput` component focuses every time it is rerendered. We use an async gen
```jsx
function *FocusingInput(props) {
- let input;
for (props of this) {
- input = yield ;
+ const input = yield ;
// This line does not execute until the component is rerendered.
input.focus();
}
}
```
-The problem is that sync generator components suspend at the point of yield expressions and only resume when updated by the parent or by a call to the `refresh` method. This means that if you were to try to access the rendered value via a `yield` expression, your code would not execute until the moment the component rerenders.
+The problem is that sync generator components suspend at the point of yield expressions and only resume when updated by the parent or by a call to the `refresh` method. This means that if you were to try to access the rendered value via a `yield` expression, your code would not execute until the moment the component rerenders. You can imagine this as the generator function above suspended exactly before the `yield` expression is assigned to the `input` variable.
To solve this problem, Crank provides an additional method on the context called `schedule`, which takes a callback and calls it with the rendered value after the component executes.
@@ -166,3 +162,18 @@ function *FocusingInput(props) {
}
}
```
+
+The `schedule` method fires the passed in callback synchronously when component finally renders. However, one unfortunate consequence of using a callback is that we lose the sequential execution of code which makes generator components so elegant and easy to understand. We can recover some of this linearity by using the `schedule` method with the `refresh` method.
+
+```jsx
+function *FocusingInput(props) {
+ this.schedule(() => this.refresh());
+ const input = yield ;
+ for (props of this) {
+ input.focus();
+ yield ;
+ }
+}
+```
+
+The focusing input now focuses before the children are yielded, but because the same input is yielded, the result is mostly the same. The `schedule` method is designed to work with the `refresh` method so that sync generator components can schedule multiple rendering passes which work synchronously.
diff --git a/website/guides/08-reusable-logic.md b/website/guides/08-reusable-logic.md
index 42b5bb3cc..0b05db7e6 100644
--- a/website/guides/08-reusable-logic.md
+++ b/website/guides/08-reusable-logic.md
@@ -4,16 +4,16 @@ title: Reusable Logic
This guide describes additional APIs as well as design patterns for developers who wish to reuse logic between components or write Crank-specific libraries.
-## Additional Context methods and properties
-Crank provides several additional methods and properties via the `Context` API to help you share logic between components. Most of the APIs demonstrated here are for library authors and should not be used in the course of typical application development.
+## Additional Context Methods and Properties
+Crank provides several additional methods and properties via the `Context` API to help you share logic between components. Some of the APIs demonstrated here are for library authors and should not be used in the course of typical application development.
### `context.props`
-The current props of a component can be accessed via the readonly context property `props`. We recommended that you access props within components via component function’s parameters or via context iterators if you’re using generator components, but the `props` property can be useful when you need to access a component’s current props from within a plugin or extension.
+The current props of a component can be accessed via the readonly context property `props`. We recommended that you access props within components via its parameters or context iterators when writing components. The `props` property can be useful when you need to access a component’s current props from within an extension or helper function.
### `context.value`
-Similarly, the most recently rendered value of a component is accessible via the readonly context property `value`. Again, we recommended that you access rendered values via the many methods described in [the guide on accessing rendered values](./lifecycles#accessing-rendered-values), but it can be useful to access the current value synchronously when writing helper context methods.
+Similarly, the most recently rendered value of a component is accessible via the readonly context property `value`. Again, we recommend that you access rendered values via the many methods described in [the guide on accessing rendered values](./lifecycles#accessing-rendered-values) or via [the `crank-ref` prop](./special-props-and-tags#crank-ref), but it can be useful to access the current value synchronously when writing helper context methods.
-Depending on what a component renders, the accessed value can be an node, a string, an array of nodes and strings, or `undefined`.
+Depending on the state of the component, the accessed value can be an node, a string, an array of nodes and strings, or `undefined`.
### Provisions
**Warning:** This API is more unstable than others, and the method names and behavior of components which use this method may change.
@@ -51,14 +51,12 @@ console.log(document.body); // "
Hello, Brian
"
Provisions allow libraries to define components which interact with their descendants without rigidly defined component hierarchies or requiring the developer to pass data manually between components as props. This makes them useful, for instance, when writing multiple components which communicate with each other, like custom `select` and `option` form elements, or drag-and-drop components.
-Anything can be passed as a key to the `get` and `set` methods, so you can use a symbol to ensure that the provision you pass between components are private and do not collide with contexts set by others.
+Anything can be passed as a key to the `get` and `set` methods, so you can use a symbol to ensure that the provision you pass between your components are private and do not collide with provisions set by others.
-**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh components when `set` is called, so it’s up to you to make sure consumers update when providers update.
+**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when `set` is called, so it’s up to you to make sure consumers update when providers update.
### `context.schedule`
-You can pass a callback to the `schedule` method to listen for when the component commits. This can be deferred if the component runs asynchronously or has async children.
-
-Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only parameter. They only fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
+You can pass a callback to the `schedule` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only parameter. They only fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
### `context.cleanup`
Similarly, you can pass a callback to the `cleanup` method to listen for when the component unmounts.
@@ -112,7 +110,7 @@ function *Counter() {
}
```
-In this example, we define the methods `setInterval` and `clearInterval` directly the `Context` prototype. The example also demonstrates caching intervals on a set which is hidden on context instances using an unexported symbol.
+In this example, we define the methods `setInterval` and `clearInterval` directly the `Context` prototype. The `setInterval` and `clearInterval` methods will now be available to all components.
**Pros:**
- Methods are available to every component automatically.
@@ -159,11 +157,11 @@ function *Counter() {
- Naming these functions can be difficult.
- No way to respond to props updates.
-Context helper utilities are useful when you want to write locally-scoped utilities, and are especially well-suited for use-cases which requires setup logic. Possible use-cases include context-aware state management utilities or wrappers around stateful APIs like [mutation observers](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or [HTML drag and drop](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).
+Context helper utilities are useful when you want to write locally-scoped utilities, and are especially well-suited for use-cases which requires setup logic. Possible use-cases include wrappers around stateful APIs like [mutation observers](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or [HTML drag and drop](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).
-### Higher-order components
+### Higher-order Components
-Because Crank components are just functions, you can write functions which both take components as parameters and return wrapped component functions.
+Because Crank components are just functions, we can write functions which both take components as parameters and return wrapped component functions.
```ts
function interval(Component) {
@@ -186,7 +184,7 @@ function interval(Component) {
const Counter = interval((props) =>
Seconds: {props.seconds}
);
```
-The interval function takes a component function and returns a component which passes the number of seconds as a prop, as well as refreshing the component whenever the interval is fired.
+The interval function takes a component function and returns a component which passes the number of seconds as a prop. Additionally, it refreshes the returned component whenever the interval is fired.
**Pros:**
- Locally scoped.
@@ -196,12 +194,12 @@ The interval function takes a component function and returns a component which p
**Cons:**
- Naming higher-order functions can be difficult.
- JavaScript doesn’t provide an easy syntax for decorating functions.
-- Props that the higher-order component pass in may clash with the component’s own expected props.
+- Props passed into the component by the wrapper may clash with the component’s own expected props.
-The main advantage of higher-order components is that you can respond to props in your utilities just like you would with a component. Higher-order components are most useful when you need reusable logic which responds to prop updates or set only well-known props. Possible use-cases include styled component or animation libraries.
+The main advantage of higher-order components is that you can respond to props in your utilities just like you would with a component. Higher-order components are most useful when you need reusable logic which responds to prop updates or sets only well-known props. Possible use-cases include styled component or animation libraries.
-### Async iterators
-Because components can be written as async generator functions, you can integrate utility functions which return async iterators seamlessly with Crank.
+### Async Iterators
+Because components can be written as async generator functions, you can integrate utility functions which return async iterators seamlessly with Crank. Async iterators are a great way to model resources because they define a standard way for releasing resources when they’re no longer needed by returning the iterator.
```ts
async function *createInterval(delay) {
@@ -240,8 +238,8 @@ async function *Counter() {
```
**Pros:**
-- The utilities you write are framework-agnostic.
-- Uniform logic to dispose of resources.
+- Async iterator utilities are framework-agnostic.
+- Uniform way to hold and release resources.
**Cons:**
- Promises and async iterators can cause race conditions and deadlocks, without any language-level features to help you debug them.
diff --git a/website/guides/09-working-with-typescript.md b/website/guides/09-working-with-typescript.md
index 7cb340271..5d0471cfd 100644
--- a/website/guides/09-working-with-typescript.md
+++ b/website/guides/09-working-with-typescript.md
@@ -4,7 +4,7 @@ title: Working with TypeScript
Crank is written in TypeScript, and provides some types out of box so you can type-check your components and elements.
-## Typing `this` in components
+## Typing `this` in Components
Trying to reference `this` in a component without a `this` type annotation will throw a type error in TypeScript‘s strict mode (you’ll see a message like `'this' implicitly has type 'any' because it does not have a type annotation`). Crank exports the `Context` class so you can annotate your components `this` as `Context`:
```tsx
@@ -25,7 +25,7 @@ function *Timer (this: Context) {
}
```
-## Typing the return types of components
+## Typing Component Return Values
You’ll often want to add a return type to your components. Crank exports custom types to help you type the return types of components:
```tsx
@@ -68,7 +68,7 @@ function *SyncGen(): Generator {
Anything assignable to `Child` can be part of the element tree, and almost anything can be assigned to `Child`.
-## Typing props
+## Typing Props
You can type the props object passed to components. This allows JSX elements which use your component as a tag to be type-checked.
```tsx
@@ -95,7 +95,7 @@ function Greeting ({name, children}: {name: string, children: Children}) {
}
```
-## Typing event listeners
+## Typing Event Listeners
If you dispatch custom events, you may want parent event listeners to be typed with the event you bubbled automatically. To do so, you can use module augmentation to extend the `EventMap` interface provided by Crank.
```tsx
@@ -122,5 +122,5 @@ function MyButton (props) {
}
```
-## Typing provisions
+## Typing Provisions
By default, calls to `Context.prototype.get` and `Context.prototype.set` will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.
diff --git a/website/guides/11-reference-for-react-developers.md b/website/guides/11-reference-for-react-developers.md
index 855b2de04..d45c2a73d 100644
--- a/website/guides/11-reference-for-react-developers.md
+++ b/website/guides/11-reference-for-react-developers.md
@@ -5,7 +5,7 @@ title: Reference for React Developers
Though Crank is inspired by React, compatibility is a non-goal, and certain concepts may be implemented using different, non-compatible APIs. The following is a reference for React developers to help them map React concepts and APIs to their equivalents in Crank.
## Class-based Components
-Crank uses functions exclusively for components; it does not provide a class-based component API. You can emulate most of React’s Component class API with the natural lifecycle of a generator function:
+Crank uses functions exclusively for components; it does not provide a class-based component API. You can emulate most of React’s class API with the natural lifecycle of generator functions.
```jsx
async function *ReactComponent(props) {
@@ -31,41 +31,41 @@ async function *ReactComponent(props) {
}
```
-This is pseudocode which demonstrates where React’s methods would be called relative to an async generator component. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.
+The example is pseudocode which demonstrates where React’s class methods would be called relative to an async generator component. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.
-The following are specific equivalents for lifecycle methods.
+The following are specific equivalents for React methods.
### `setState` and `forceUpdate`
Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](./guides/components#stateful-components).
-Crank is not “reactive” in the same sense as React, in that it does not actually track your component’s local state. You can either use `Context.prototype.refresh` to manually refresh the component, much like React’s `forceUpdate` method, or you can use async generator components, which refresh automatically whenever the returned async generator object yields.
+Crank is not “reactive” in the same sense as React, in that it does not actually track your component’s local state. You can either use `Context.prototype.refresh` to manually refresh the component, much like React’s `forceUpdate` method, or you can use async generator components, which refresh automatically whenever the returned async generator yields.
### `defaultProps`
Crank doesn’t have a `defaultProps` implementation. Instead, you can provide default values when destructuring props. [See the guide on default props](./components#default-props).
-### `shouldComponentUpdate`
-Components themselves do not provide a way to prevent updates to themselves. Instead, you can use `Copy` elements to prevent the rerendering of a specific subtree. [Refer to the description of `Copy` elements](./special-props-and-tags#copy) for more information.
-
### `componentWillMount` and `componentDidMount`
Setup code for components can be written at the top of generator components. It will not execute until the component is mounted in the tree.
+### `shouldComponentUpdate`
+As an alternative to React’s `shouldComponentUpdate` method, you can use `Copy` elements to prevent the rerendering of a specific subtree. Refer to [the description of `Copy` elements](./special-props-and-tags#copy) for more information.
+
### `getDerivedStateFromProps`, `componentWillUpdate` and `getSnapshotBeforeUpdate`
-Code which compares old and new props or state and performs side-effects can be written directly in your components. See the section on [`prop updates`](./components#props-updates/) for examples of comparing old and new props.
+Code which compares old and new props or state can be written directly in your components. See the section on [`prop updates`](./components#comparing-old-and-new-props) for an example of a component which compares old and new props.
-### `shouldComponentUpdate`
-Crank uses the special `Copy` element to prevent child updates. See the guide on the `Copy` element to see how you might reimplement `React.memo` directly in user space.
+### `componentDidUpdate`
+To execute code after rendering, you can use async generator components or [the `schedule` method](./api-reference#schedule). See [the guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
### `componentWillUnmount`
-You can use `try`/`finally` to run code when the component is unmounted. You can also use the [`Context.prototype.cleanup`] method if you’re writing extensions which don’t run in the main execution of the component.
+You can use a `try`/`finally` block to run code when a component is unmounted. You can also use [the `cleanup` method](./api-reference#cleanup) if you’re writing extensions which don’t run in the main execution of the component.
### `componentDidCatch`
-To catch errors which occur in child components, you can use generator components and wrap `yield` operations in a `try`/`catch` block. Refer to [the relevant section in guides](#TK).
+To catch errors which occur in child components, you can use generator components and wrap `yield` operations in a `try`/`catch` block. Refer to [the relevant guide on catching errors](./lifecycles#catching-errors).
## Hooks
Crank does not implement any APIs similar to React Hooks. The following are alternatives to specific hooks.
### `useState` and `useReducer`
-Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](#TTKTKTKTKTKTKTK).
+Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](./components#stateful-components).
### `useEffect` and `useLayoutEffect`
Crank does not have any requirements that rendering should be “pure.” In other words, you can trigger side-effects directly while rendering because Crank does not execute components more times than you might expect.
@@ -74,82 +74,59 @@ Crank does not have any requirements that rendering should be “pure.” In oth
Because the execution of generator components is preserved, there is no need to “memoize” or “cache” callbacks or other values. You can simply assign them to a constant variable.
### `useImperativeHandle`
-Crank does not yet have a way to access component instances, and parent components should not access child contexts directly. An imperative wrapper which uses web components is planned.
+Crank does not have a way to access component instances, and parent components should not access child components directly. A wrapper which uses web components for the definition of custom elements with imperative methods and properties is planned.
### Custom Hooks
-The main appeal of hooks for library authors is that you can encapsulate shared logic in hooks. Refer to [this guide on reusable logic](./reusable-logic) for Crank-specific APIs and strategies.
+The main appeal of hooks for library authors is that you can encapsulate entire APIs in one or two hooks. Refer to [the guide on reusable logic](./reusable-logic#strategies-for-reusing-logic) for various patterns and strategies to wrap APIs and logic in Crank.
## Suspense and Concurrent Mode
-Crank does not implement any sort of Suspense-like API. As an alternative, you can use async functions and async generator functions directly. See the [guide on async components](./async-components) for an introduction to async components, as well as a demonstration of how you can implement the `Suspense` component directly in user space.
+Crank uses async functions and promises for scheduling and coordinating async processes. See the [guide on async components](./async-components) for an introduction to async components, as well as a demonstration of how you can implement the `Suspense` component directly in user space.
## PropTypes
-Crank is written in TypeScript, and you can add type checking to component elements by typing the props parameter of the component function.
+Crank is written in TypeScript, and you can add type checking to components by typing the props parameter of the component function. See [the guide on TypeScript](./working-with-typescript) for detailed instructions on how to type components.
+
+## Array Children
+Crank does not restrict children in JSX elements to just arrays. You can interpolate ES6 maps, sets or any other iterable into your Crank elements. Additionally, Crank does not warn you if elements in the iterable are unkeyed.
-## Fragments and array children
- There’s no reason to restrict children in JSX elements to just arrays. You can interpolate ES6 Maps, Sets or any other user-defined iterable into your Crank elements, and Crank will simply render them in an implicit `Fragment`.
+## Fragments
+The [`Fragment` element](./special-props-and-tags#fragment) works almost exactly the same as it does in React, except in Crank you can also use a callback ref to access its contents.
## `React.cloneElement`
-You can clone elements using the `cloneElement` function.
+You can clone elements using [the `cloneElement` function](./api-reference#cloneelement).
## `ReactDOM.createPortal`
-The `createPortal` function is replaced by a special `Portal` element, whose behavior and expected props varies according to the target rendering environment. Refer to [the guide on the `Portal` element](#KTKTKTKT) for more information.
+The `createPortal` function is replaced by the special `Portal` element, whose behavior and expected props varies according to the target rendering environment. Refer to [the guide on the `Portal` element](./special-props-and-tags#portal) for more information.
## `React.memo`
-You can use `Copy` elements to implement `React.memo` in user space:
-```jsx
-function equals(props, newProps) {
- for (const name in {...props, ...newProps}) {
- if (props[name] !== newProps[name]) {
- return false;
- }
- }
-
- return true;
-}
-
-function memo(Component) {
- return function *Wrapped({props}) {
- yield ;
- for (const newProps of this) {
- if (equals(props, newProps)) {
- yield ;
- } else {
- yield ;
- }
-
- props = newProps;
- }
- };
-}
-```
-
-See [the guide on component elements](#TKTKTKTK) for more information.
+See [the guide on `Copy` elements](./special-props-and-tags#copy) for a demonstration of how you can use the `Copy` tag to implement `React.memo` in user space.
## DOM element props
The following are a list of the differences in APIs when passings props to DOM elements.
### `className` and `htmlFor`
-We prefer attribute names, rather than the JS property equivalents when the two diverge.
+We prefer attribute names rather than the JS property equivalents when the two diverge.
```jsx
````
+See [the section on prop naming conventions](./special-props-and-tags#prop-naming-conventions) for more information.
+
### `style`
+The `style` prop value can be an object of CSS declarations. However, unlike React, CSS property names match the case of their CSS equivalents, and we do not add units to numbers. Additionally, Crank allows the style prop to be a CSS string as well.
+
```jsx
Hello
```
-The `style` prop value can be a `cssText` string, or an object, similar to React. Unlike React, the CSS property names match the case of their CSS equivalents, and we do not add units to numbers.
-
### Event props
-Host elements can be listened to using `onevent` props, but the prop name is always lowercase. Crank also provides an `EventTarget` API for components to add and remove event listeners from the top-level nodes of each component. In both cases, Crank does not use a synthetic event system or polyfill events in any way. See [the guide on events for more information](./handling-events).
+Host elements can be listened to using `onevent` props, but the prop name is always lowercase. Crank also provides an `EventTarget` API for components to add and remove event listeners from the top-level node or nodes of each component. In both cases, Crank does not use a synthetic event system or polyfill events in any way. Refer to [the guide on event handling](./handling-events).
### Controlled and Uncontrolled Props
-Crank does not have a concept of controlled or uncontrolled props, and does not provide `defaultValue`-style props for DOM elements.
+Crank does not have a concept of controlled or uncontrolled props, and does not provide `defaultValue`-style props for DOM elements. See [the section on form elements](./handling-events#form-elements) for a detailed description of how Crank handles form elements.
### `dangerouslySetInnerHTML`
-Host DOM elements accept an `innerHTML` prop; it does not provide the `dangerouslySetInnerHTML={{__html}}` API from React. Alternatively, you can use the special `Raw` tag to insert HTML strings or even nodes into the tree without a parent. [See the guide on the `Raw` element](./special-props-and-tag#raw) for more information.
+Host DOM elements accept an `innerHTML` prop; Crank does not provide the `dangerouslySetInnerHTML={{__html}}` API like React. Alternatively, you can use the special `Raw` tag to insert HTML strings or even DOM nodes directly into an element tree without a parent. [See the guide on the `Raw` element](./special-props-and-tag#raw) for more information.
## Keys
Crank provides keyed rendering via the `crank-key` prop. The prop was renamed because “key” is a common word and because the prop is not erased from the props object passed into components.
@@ -157,7 +134,9 @@ Crank provides keyed rendering via the `crank-key` prop. The prop was renamed be
Keys work similarly to the way they do in React. The main difference is that Crank does not warn about unkeyed elements which appear in arrays or iterables.
## Refs
-Crank provides the callback-style ref API from React via the `crank-ref` prop. See the [guide on the `crank-ref` prop](./special-props-and-tags#crank-ref). You can also access values many other ways. See the [guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
+Crank provides the callback-style ref API from React via the `crank-ref` prop. Unlike React, all elements can be read using the `crank-ref` prop, including Fragments, and . See the [guide on the `crank-ref` prop](./special-props-and-tags#crank-ref).
+
+You can also access values many other ways. See the [guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
## React Contexts
-Because we refer to the `this` keyword as the component’s “context,” we refer to the equivalent concept of [React Context API](https://reactjs.org/docs/context.html) as “provisions” instead. We use the methods `get` and `set` to define provisions between components. See [the guide on provisions](./reusable-logic#provisions) for more information.
+Because we refer to the `this` keyword of components as “the component’s context” (“controller” would have been three more characters), we refer to the equivalent concept of [React’s Context API](https://reactjs.org/docs/context.html) as “provisions” instead. We use the methods `get` and `set` to define provisions between components. See [the guide on provisions](./reusable-logic#provisions) for more information.
diff --git a/website/guides/12-api-reference.md b/website/guides/12-api-reference.md
index 1826ff171..17f3c80be 100644
--- a/website/guides/12-api-reference.md
+++ b/website/guides/12-api-reference.md
@@ -154,38 +154,46 @@ A special element tag which copies whatever child appeared previously in the ele
A special element tag for creating a new element subtree with a different root, passed via the root prop.
**Props:**
-- root - A root to render into.
+- `root: TRoot` - A root to render into.
### `Raw`
A special element tag for injecting raw nodes into an element tree via its value prop.
**Props:**
-- value - TODO
+- `value: TNode | string` - A string or a node. If the value is a node, it will be inserted directly into the tree. If the value is a string, the renderer will parse the string and insert the parsed result into the tree.
## Functions
### `createElement`
+Creates an element with the specified tag, props and children. This function is typically used as a target for JSX transpilers, rather than called directly.
+
**Parameters:**
-- `tag: string | symbol | Component` - TODO
-- `props: Record` - TODO
-- `...children: Children` - TODO
+- `tag: string | symbol | Component`
+- `props: Record`
+- `...children: Children`
-Creates an element with the specified tag, props and children. This function is typically used as a target for JSX transpilers, rather than called directly.
+**Return Value:**
+
+An element.
### `isElement`
+
**Parameters:**
- `value: unknown` - The value to be tested.
-Returns true if the value passed in is a Crank Element.
+**Return Value:**
+
+A boolean which indicates the value passed in is a Crank Element.
### `cloneElement`
+Clones the passed in element. Throws a `TypeError` if the value passed in is not an element.
+
**Parameters:**
- `element: Element` - The Element to be cloned.
-Clones the passed in element.
+**Return Value:**
-**Remarks:**
-Throws a `TypeError` if the value passed in is not an element.
+A copy of the element passed in.
## Classes
### `Element`
@@ -195,7 +203,7 @@ Elements are the basic building blocks of Crank applications. They are JavaScrip
- `tag: Tag` - The tag of the element.
- `props: Record` - The props of the element.
- `key: unknown` - A key by which to identify the element during the element diffing process.
-- `ref: Function` - A callback which fires whenever the element is rendered.
+- `ref: Function` - A callback which fires with the element’s rendered value whenever the element is rendered.
#### Methods
- [`constructor`](#Element.constructor)
@@ -203,6 +211,8 @@ Elements are the basic building blocks of Crank applications. They are JavaScrip
### `Renderer`
An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
+**NOTE:** The internal Crank renderer methods documented in the [guide on custom renderers.](./custom-renderers)
+
**Type Parameters:**
- `TNode`
- `TScope`
@@ -219,8 +229,6 @@ An abstract class which is subclassed to render to different target environments
- `root?: TRoot` - The renderer specific root to render into.
- `ctx?: Context` - A context which will parent the contexts of all top-level components in the element tree.
-**NOTE:** The internal Crank renderer methods documented in the [guide on custom renderers.](./custom-renderers)
-
### `Context`
A class which is instantiated and passed to every component function as its `this` value.
@@ -229,22 +237,24 @@ A class which is instantiated and passed to every component function as its `thi
- `TResult` - The expected result from rendering the component.
#### Properties
-- `props` - **Readonly** The current props of the component.
-- `value` - **Readonly** The current rendered value of the component.
+- `props` - The current props of the component. **Readonly**
+- `value` - The current rendered value of the component. **Readonly**
#### Methods
- `[Symbol.iterator]`
Returns an iterator of the props passed to the component. Only used in generator components.
+
- `[Symbol.asyncIterator]`
Returns an async iterator of the props passed to the component. Only used in generator
-- `get`
-- `set`
+
- `refresh`
Updates the component in place.
+
- `schedule`
Executes the passed in callback when the component renders. The `schedule` method will only fire once for each call and callback.
**Parameters:**
- `callback: Function` - The callback to be executed.
+
- `cleanup`
Executes the passed in callback when the component unmounts. The `cleanup` method will only fire once for each call and callback.
**Parameters:**
@@ -263,11 +273,14 @@ A class which is instantiated and passed to every component function as its `thi
- `passive: boolean` - If true, calling the `preventDefault` method on the event will have no effect. Using this flag can increase performance. It is most useful for event types like `scroll` which fire frequently and are rarely cancelled.
- `removeEventListener`
+ Removes an event listener by type, listener and capture option.
+
**Parameters:**
- `type: string` - The type of the event.
- `listener: Function` - The listener callback.
- `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
- `capture: boolean` - If true, will remove listeners which use the capture option.
+
- `dispatchEvent`
Dispatches an event on the context. Will propagate to parent contexts according to the same event bubbling and capture algorithms used by the DOM.
@@ -277,3 +290,20 @@ A class which is instantiated and passed to every component function as its `thi
**Return Value:**
The return value is `false` if the event is cancelable and an event listener calls the `preventDefault` method on the event, and `true` otherwise.
+
+- `get`
+ Retrieves a provision set by an ancestor by key.
+
+ **Parameters:**
+ - `key: unknown` - The key of the provision.
+
+ **Return Value:**
+
+ The provision by key, or undefined if no provision is found.
+
+- `set`
+ Sets a provision by key for descendant components.
+
+ **Parameters:**
+ - `key: unknown` - The key of the provision.
+ - `value: unknown` - The value of the provision.
diff --git a/website/src/index.css b/website/src/index.css
index a465236a6..e69c48138 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -4,6 +4,10 @@
box-sizing: border-box;
}
+html {
+ scroll-behavior: smooth;
+}
+
body {
background-color: #0c1227;
color: #f5f9ff;
@@ -52,6 +56,7 @@ a {
}
&.anchor {
+ scroll-margin-top: 5em;
outline: none;
&::before {
content: "";
@@ -210,6 +215,7 @@ li {
.footer {
margin: 8em auto 0;
padding: 3em 0;
+ line-height: 1.4em;
border-top: 1px solid #f5f9ff;
color: #bdc2c7;
text-align: center;
From 579a933c7fb7f3cfa73348db07df4e0ea8672c02 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Mon, 20 Jul 2020 14:55:12 -0400
Subject: [PATCH 16/26] markdown really do be user hostile
---
website/guides/12-api-reference.md | 152 +++++++++++++++++------------
1 file changed, 91 insertions(+), 61 deletions(-)
diff --git a/website/guides/12-api-reference.md b/website/guides/12-api-reference.md
index 17f3c80be..b4bbfb5ed 100644
--- a/website/guides/12-api-reference.md
+++ b/website/guides/12-api-reference.md
@@ -4,6 +4,7 @@ title: API Reference
## Types
### `Tag`
+
A type which represents all valid values which can be used for the tag of an element.
**Example:**
@@ -26,6 +27,7 @@ tag = {};
```
### `TagProps`
+
A helper type to map the tag of an element to its expected props.
**Type Parameters:**
@@ -48,6 +50,7 @@ props = {name: 1000};
```
### `Child`
+
A type which describes all valid singular values of an element tree.
**Example:**
@@ -77,6 +80,7 @@ child = Symbol("Hello");
```
### `Children`
+
A type which describes all valid values of an element tree, including arbitrarily nested iterables of such values.
**Example:**
@@ -104,6 +108,7 @@ children = Symbol("Hello");
```
### `Component`
+
A type which represents all functions which can be used as a component.
**Type Parameters:**
@@ -116,6 +121,7 @@ A helper type which repesents all the possible rendered values of an element.
- `TNode` - The node type for the element as created by the renderer.
### `EventMap`
+
An interface which maps Event type strings to event subclasses. Can be extended via TypeScript module augmentation for strongly typed event listeners.
**Example:**
@@ -130,6 +136,7 @@ declare global {
```
### `ProvisionMap`
+
An interface which can be extended to provide strongly typed provisions. See `Context.prototype.get` and `Context.prototype.set`.
**Example:**
@@ -145,19 +152,23 @@ declare global {
## Special Tags
### `Fragment`
+
A special element tag for grouping multiple children within a parent.
### `Copy`
+
A special element tag which copies whatever child appeared previously in the element’s position.
### `Portal`
+
A special element tag for creating a new element subtree with a different root, passed via the root prop.
**Props:**
-- `root: TRoot` - A root to render into.
+- `root: TRoot` - A root to render into. It should be of the same type as the second argument passed to the `render` method.
### `Raw`
+
A special element tag for injecting raw nodes into an element tree via its value prop.
**Props:**
@@ -165,6 +176,7 @@ A special element tag for injecting raw nodes into an element tree via its value
## Functions
### `createElement`
+
Creates an element with the specified tag, props and children. This function is typically used as a target for JSX transpilers, rather than called directly.
**Parameters:**
@@ -186,6 +198,7 @@ An element.
A boolean which indicates the value passed in is a Crank Element.
### `cloneElement`
+
Clones the passed in element. Throws a `TypeError` if the value passed in is not an element.
**Parameters:**
@@ -196,19 +209,23 @@ Clones the passed in element. Throws a `TypeError` if the value passed in is not
A copy of the element passed in.
## Classes
+
### `Element`
+
Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by renderers to produce and manage stateful nodes.
-#### Properties
+#### Methods
+#### `constructor`
+Constructs an element. Typicially, you would use the `createElement` function rather than calling this constructor directly.
+
+**Parameters:**
- `tag: Tag` - The tag of the element.
- `props: Record` - The props of the element.
- `key: unknown` - A key by which to identify the element during the element diffing process.
- `ref: Function` - A callback which fires with the element’s rendered value whenever the element is rendered.
-#### Methods
-- [`constructor`](#Element.constructor)
-
### `Renderer`
+
An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
**NOTE:** The internal Crank renderer methods documented in the [guide on custom renderers.](./custom-renderers)
@@ -220,14 +237,16 @@ An abstract class which is subclassed to render to different target environments
- `TResult`
#### Methods
-- `constructor`
- Creates a new Renderer. The base `Renderer` constructor accepts no parameters.
+#### `constructor`
+
+Creates a new Renderer. The base `Renderer` constructor accepts no parameters.
-- `render`
- **Parameters:**
- - `children: Children` - The element tree to render.
- - `root?: TRoot` - The renderer specific root to render into.
- - `ctx?: Context` - A context which will parent the contexts of all top-level components in the element tree.
+#### `render`
+
+**Parameters:**
+- `children: Children` - The element tree to render.
+- `root?: TRoot` - The renderer specific root to render into.
+- `ctx?: Context` - A context which will parent the contexts of all top-level components in the element tree.
### `Context`
A class which is instantiated and passed to every component function as its `this` value.
@@ -241,69 +260,80 @@ A class which is instantiated and passed to every component function as its `thi
- `value` - The current rendered value of the component. **Readonly**
#### Methods
-- `[Symbol.iterator]`
- Returns an iterator of the props passed to the component. Only used in generator components.
+#### `[Symbol.iterator]`
+
+Returns an iterator of the props passed to the component. Only used in generator components.
+
+#### `[Symbol.asyncIterator]`
+
+Returns an async iterator of the props passed to the component. Only used in generator
+
+#### `refresh`
+
+Updates the component in place.
-- `[Symbol.asyncIterator]`
- Returns an async iterator of the props passed to the component. Only used in generator
+#### `schedule`
-- `refresh`
- Updates the component in place.
+Executes the passed in callback when the component renders. The `schedule` method will only fire once for each call and callback.
-- `schedule`
- Executes the passed in callback when the component renders. The `schedule` method will only fire once for each call and callback.
- **Parameters:**
- - `callback: Function` - The callback to be executed.
+**Parameters:**
+- `callback: Function` - The callback to be executed.
-- `cleanup`
- Executes the passed in callback when the component unmounts. The `cleanup` method will only fire once for each call and callback.
- **Parameters:**
- - `callback: Function` - The callback to be executed.
+#### `cleanup`
-- `addEventListener`
- Adds an event listener to the context. The `addEventListener` method will also attach event listeners to the underlying top-level node or nodes which were created for the component.
- - `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
- - `capture: boolean` - Indicates the listener should execute during [the capture phase](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) of event dispatch.
- **Parameters:**
- - `type: string` - The type of the event.
- - `listener: Function` - The listener callback.
- - `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
- - `capture: boolean` - If true, the event listener will fire during [the capture phase](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) of event dispatch.
- - `once: boolean` - If true, the event listener will fire at most once before being removed.
- - `passive: boolean` - If true, calling the `preventDefault` method on the event will have no effect. Using this flag can increase performance. It is most useful for event types like `scroll` which fire frequently and are rarely cancelled.
+Executes the passed in callback when the component unmounts. The `cleanup` method will only fire once for each call and callback.
-- `removeEventListener`
- Removes an event listener by type, listener and capture option.
+**Parameters:**
+- `callback: Function` - The callback to be executed.
- **Parameters:**
- - `type: string` - The type of the event.
- - `listener: Function` - The listener callback.
- - `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
- - `capture: boolean` - If true, will remove listeners which use the capture option.
+#### `addEventListener`
-- `dispatchEvent`
- Dispatches an event on the context. Will propagate to parent contexts according to the same event bubbling and capture algorithms used by the DOM.
+Adds an event listener to the context. The `addEventListener` method will also attach event listeners to the underlying top-level node or nodes which were created for the component.
- **Parameters:**
- - `ev: Event` - An Event object.
+**Parameters:**
+- `type: string` - The type of the event.
+- `listener: Function` - The listener callback.
+- `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
+ - `capture: boolean` - If true, the event listener will fire during [the capture phase](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) of event dispatch.
+ - `once: boolean` - If true, the event listener will fire at most once before being removed.
+ - `passive: boolean` - If true, calling the `preventDefault` method on the event will have no effect. Using this flag can increase performance. It is most useful for event types like `scroll` which fire frequently and are rarely cancelled.
- **Return Value:**
+#### `removeEventListener`
- The return value is `false` if the event is cancelable and an event listener calls the `preventDefault` method on the event, and `true` otherwise.
+Removes an event listener by type, listener and capture option.
-- `get`
- Retrieves a provision set by an ancestor by key.
+**Parameters:**
+- `type: string` - The type of the event.
+- `listener: Function` - The listener callback.
+- `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
+ - `capture: boolean` - If true, will remove listeners which use the capture option.
- **Parameters:**
- - `key: unknown` - The key of the provision.
+#### `dispatchEvent`
- **Return Value:**
+Dispatches an event on the context. Will propagate to parent contexts according to the same event bubbling and capture algorithms used by the DOM.
- The provision by key, or undefined if no provision is found.
+**Parameters:**
+- `ev: Event` - An Event object.
-- `set`
- Sets a provision by key for descendant components.
+**Return Value:**
- **Parameters:**
- - `key: unknown` - The key of the provision.
- - `value: unknown` - The value of the provision.
+The return value is `false` if the event is cancelable and an event listener calls the `preventDefault` method on the event, and `true` otherwise.
+
+#### `get`
+
+Retrieves a provision set by an ancestor by key.
+
+**Parameters:**
+- `key: unknown` - The key of the provision.
+
+**Return Value:**
+
+The provision by key, or undefined if no provision is found.
+
+#### `set`
+
+Sets a provision by key for descendant components.
+
+**Parameters:**
+- `key: unknown` - The key of the provision.
+- `value: unknown` - The value of the provision.
From 049b917e4c070bda6fa8722f5134597d46f16161 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Mon, 20 Jul 2020 17:17:06 -0400
Subject: [PATCH 17/26] fix attribution
---
website/build.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/build.tsx b/website/build.tsx
index 509409d04..c80c619d3 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -179,7 +179,7 @@ function Footer(): Element {
);
From 1743fbb5135f6f89460f9aa04699cc2a3d703b01 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 21 Jul 2020 00:27:05 -0400
Subject: [PATCH 18/26] update guides on provisions
---
website/guides/08-reusable-logic.md | 10 +++++-----
website/guides/09-working-with-typescript.md | 2 +-
website/guides/11-reference-for-react-developers.md | 2 +-
website/guides/12-api-reference.md | 6 +++---
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/website/guides/08-reusable-logic.md b/website/guides/08-reusable-logic.md
index 0b05db7e6..2c7c2562c 100644
--- a/website/guides/08-reusable-logic.md
+++ b/website/guides/08-reusable-logic.md
@@ -18,16 +18,16 @@ Depending on the state of the component, the accessed value can be an node, a st
### Provisions
**Warning:** This API is more unstable than others, and the method names and behavior of components which use this method may change.
-Crank allows you to provide data to all of a component’s descendants via the methods `get` and `set`. The `set` method sets a “provision” under a specific key, and the `get` method retrieves the value set under a specific key by the nearest ancestor.
+Crank allows you to provide data to all of a component’s descendants via the methods `provide` and `consume`. The `provide` method sets a “provision” under a specific key, and the `consume` method retrieves the value set under a specific key by the nearest ancestor.
```ts
function GreetingProvider({greeting, children}) {
- this.set("greeting", greeting);
+ this.provide("greeting", greeting);
return children;
}
function Greeting({name}) {
- const greeting = this.get("greeting");
+ const greeting = this.consume("greeting");
return
"
Provisions allow libraries to define components which interact with their descendants without rigidly defined component hierarchies or requiring the developer to pass data manually between components as props. This makes them useful, for instance, when writing multiple components which communicate with each other, like custom `select` and `option` form elements, or drag-and-drop components.
-Anything can be passed as a key to the `get` and `set` methods, so you can use a symbol to ensure that the provision you pass between your components are private and do not collide with provisions set by others.
+Anything can be passed as a key to the `provide` and `consume` methods, so you can use a symbol to ensure that the provision you pass between your components are private and do not collide with provisions set by others.
-**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when `set` is called, so it’s up to you to make sure consumers update when providers update.
+**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when `provide` is called, so it’s up to you to make sure consumers update when providers update.
### `context.schedule`
You can pass a callback to the `schedule` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only parameter. They only fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
diff --git a/website/guides/09-working-with-typescript.md b/website/guides/09-working-with-typescript.md
index 5d0471cfd..1a1cef92a 100644
--- a/website/guides/09-working-with-typescript.md
+++ b/website/guides/09-working-with-typescript.md
@@ -123,4 +123,4 @@ function MyButton (props) {
```
## Typing Provisions
-By default, calls to `Context.prototype.get` and `Context.prototype.set` will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.
+By default, calls to the context’s `provide` and `consume` methods will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.
diff --git a/website/guides/11-reference-for-react-developers.md b/website/guides/11-reference-for-react-developers.md
index d45c2a73d..4414d8c06 100644
--- a/website/guides/11-reference-for-react-developers.md
+++ b/website/guides/11-reference-for-react-developers.md
@@ -139,4 +139,4 @@ Crank provides the callback-style ref API from React via the `crank-ref` prop. U
You can also access values many other ways. See the [guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
## React Contexts
-Because we refer to the `this` keyword of components as “the component’s context” (“controller” would have been three more characters), we refer to the equivalent concept of [React’s Context API](https://reactjs.org/docs/context.html) as “provisions” instead. We use the methods `get` and `set` to define provisions between components. See [the guide on provisions](./reusable-logic#provisions) for more information.
+Because we refer to the `this` keyword of components as “the component’s context” (“controller” would have been three more characters), we refer to the equivalent concept of [React’s Context API](https://reactjs.org/docs/context.html) as “provisions” instead. We use the context methods `provide` and `consume` to define provisions between ancestor and descendant components. See [the guide on provisions](./reusable-logic#provisions) for more information.
diff --git a/website/guides/12-api-reference.md b/website/guides/12-api-reference.md
index b4bbfb5ed..ceed22f2b 100644
--- a/website/guides/12-api-reference.md
+++ b/website/guides/12-api-reference.md
@@ -137,7 +137,7 @@ declare global {
### `ProvisionMap`
-An interface which can be extended to provide strongly typed provisions. See `Context.prototype.get` and `Context.prototype.set`.
+An interface which can be extended to provide strongly typed provisions. See `Context.prototype.provide` and `Context.prototype.consume`.
**Example:**
```ts
@@ -319,7 +319,7 @@ Dispatches an event on the context. Will propagate to parent contexts according
The return value is `false` if the event is cancelable and an event listener calls the `preventDefault` method on the event, and `true` otherwise.
-#### `get`
+#### `consume`
Retrieves a provision set by an ancestor by key.
@@ -330,7 +330,7 @@ Retrieves a provision set by an ancestor by key.
The provision by key, or undefined if no provision is found.
-#### `set`
+#### `provide`
Sets a provision by key for descendant components.
From 7d7e6c85d6ca5b5872778690ec80895bed2fa0e5 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 21 Jul 2020 00:31:40 -0400
Subject: [PATCH 19/26] tweak logo css
---
website/src/index.css | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/website/src/index.css b/website/src/index.css
index e69c48138..358e6ee4d 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -114,11 +114,12 @@ li {
}
.navbar-logo {
+ width: 1.9em;
+ height: 1.9em;
background-color: transparent;
display: inline-block;
- height: 30px;
margin-top: -2px;
- margin-right: 0.5rem;
+ margin-right: 0.5em;
}
.home {
From 55c04a4cc42e48590acbe7938ce663eddb0a4cdd Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 21 Jul 2020 01:22:56 -0400
Subject: [PATCH 20/26] update custom renderer docs
---
website/guides/06-special-props-and-tags.md | 8 +-
website/guides/10-custom-renderers.md | 105 ++++++++++--------
.../11-reference-for-react-developers.md | 2 +-
3 files changed, 65 insertions(+), 50 deletions(-)
diff --git a/website/guides/06-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
index f7596b06f..c6dca80be 100644
--- a/website/guides/06-special-props-and-tags.md
+++ b/website/guides/06-special-props-and-tags.md
@@ -138,7 +138,7 @@ You can still use the `className` and `htmlFor` props as well, but using the for
Crank provides four element tags which have special meaning to the renderer, and affect element diffing and rendering output in various ways.
-### Fragment
+### `Fragment`
Crank provides a `Fragment` tag, which allows you to render multiple children into a parent without wrapping them in another DOM node. Under the hood, iterables which appear in the element tree are also implicitly wrapped in a `Fragment` element by the renderer.
```jsx
@@ -158,7 +158,7 @@ console.log(document.body.innerHTML);
// "
Sibling 1
Sibling 2
"
```
-### Copy
+### `Copy`
It‘s often fine to rerender Crank components, because elements are diffed, persistent between renders, and unnecessary mutations usually avoided. However, you might want to prevent a child from updating when the parent rerenders, perhaps because a certain prop hasn’t changed, because you want to batch updates from the parent, or as a performance optimization. To do this, you can use the `Copy` tag to indicate to Crank that you don’t want to update a previously rendered element in that same position.
```jsx
@@ -190,7 +190,7 @@ function memo(Component) {
In this example, `memo` is a higher-order component, a function which takes a component and returns a component. This wrapper component compares old and new props and yields a `Copy` element if every prop is shallowly equal. A `Copy` element can appear anywhere in an element tree to prevent rerenderings, and the only props `Copy` elements take are the `crank-key` and `crank-ref` props, which work as expected.
-### Portal
+### `Portal`
Sometimes you may want to render into a DOM node which isn’t the current parent element, or even a part of the currently rendered DOM tree. You can do this with the `Portal` tag, passing in a DOM node as its `root` prop. The Portal’s children will be rendered into the specified root element, just as if Renderer.render was called with the root value as its second argument.
```jsx
@@ -219,7 +219,7 @@ console.log(root2.innerHTML);
This tag is useful for creating modals or tooltips, which usually need to be rendered into separate DOM elements at the bottom of the page for visibility reasons. Events dispatched from a `Portal` element‘s child components via the `dispatchEvent` method will still bubble into parent components.
-### Raw
+### `Raw`
Sometimes, you may want to insert raw HTML or actual DOM nodes directly into the element tree. Crank allows you to do this with the `Raw` element. The `Raw` element takes a `value` prop which is interpreted by the renderer. For the DOM renderer, if `value` is an HTML string, the renderer will parse and insert the resulting DOM nodes. If the value is already a DOM node, Crank will insert them in place.
```jsx
diff --git a/website/guides/10-custom-renderers.md b/website/guides/10-custom-renderers.md
index a31878dd4..37f9aa729 100644
--- a/website/guides/10-custom-renderers.md
+++ b/website/guides/10-custom-renderers.md
@@ -2,7 +2,7 @@
title: Custom Renderers
---
-The core Crank module provides an abstract `Renderer` class which can be extended to produce more than just DOM nodes or HTML strings, allowing you to target alternative environments such as WebGL-based libraries, terminals, smartphones or smart TVs. This guide provides an overview of the concepts and internal methods which you will need to know when implementing a custom renderer yourself. Alternatively, you can read through the [DOM](https://github.com/bikeshaving/crank/blob/master/src/dom.ts?ts=2) and [HTML](https://github.com/bikeshaving/crank/blob/master/src/html.ts?ts=2) renderer implementations to learn by example.
+The core Crank module provides an abstract `Renderer` class which can be extended to produce more than just DOM nodes or HTML strings, allowing you to target alternative environments such as WebGL libraries like Three.js or Pixi.js, terminals, smartphones or smart TVs. This guide provides an overview of the concepts and internal methods which you will need to know when implementing a custom renderer yourself. Alternatively, you can read through the [DOM](https://github.com/bikeshaving/crank/blob/master/src/dom.ts?ts=2) and [HTML](https://github.com/bikeshaving/crank/blob/master/src/html.ts?ts=2) renderer implementations to learn by example.
**Warning:** The custom renderer API is currently unstable both because of its performance-sensitive nature and because the exact complications of rendering to a wide variety of environments are not yet fully known. If you maintain a Crank renderer, you *will* have to deal with breaking changes as Crank is optimized and as new renderer requirements are discovered.
@@ -12,28 +12,39 @@ Crank does not provide lifecycle methods or hooks as part of its public interfac
Rendering is essentially a depth-first walk of an element tree, where we recursively compare new elements to what was previously rendered. When elements are new to the tree, we *mount* the element, when elements have been seen before, we *update* the element, and when elements no longer exist in the tree, they are *unmounted.*
-*Committing* is the part of the rendering process where we actually perform the operations which create, mutate and dispose of nodes. Elements are committed in a *post-order traversal* of the tree, meaning that by the time a specific element is committed, all of its children will have already committed as well. This is done so that rendering side-effects happen all at once, even if there are async components in the tree, leading to a more consistent and performant user experience. By contrast, components can be thought of as executing in a *pre-order traversal* of the tree, because the only way to get the children of a component element is to execute the component.
+*Committing* is the part of the rendering process where we actually perform the operations which create, mutate and dispose of nodes. Elements are committed in a *post-order traversal* of the tree, meaning that by the time a specific element is committed, all of its children will already have been committed as well. This is done so that rendering side-effects happen all at once, even if there are async components in the tree, leading to a more consistent and performant user experience. By contrast, components can be thought of as executing in a *pre-order traversal* of the tree, because the only way to get the children of a component element is to execute the component.
## Renderer Type Parameters
**Note:** Renderer development is considerably more abstract than application development, and using the TypeScript types provided by Crank can make this process much easier to understand. Therefore, this guide both assumes familiarity with TypeScript and uses TypeScript syntax in its examples.
-The renderer class takes four type parameters which describe the types of values as they flow in and out of the custom renderer methods.
+The `Renderer` class takes four type parameters which describe the types of values as they flow in and out of the custom renderer methods.
```ts
class Renderer<
- TNode extends object,
+ TNode,
TScope = unknown,
- TRoot extends object = TNode,
+ TRoot = TNode,
TResult = ElementValue
>
```
-- `TNode` is the most important type: it is the type of the node associated with each host element. For instance, for the basic DOM renderer, TNode is the [DOM Node interface](https://developer.mozilla.org/en-US/docs/Web/API/Node).
-- `TScope` is the type of the *scope*, a renderer-specific concept for arbitrary data which can passed down the tree between host elements. Scopes are useful for passing contextual information down the tree to be used when nodes are created; for instance, the DOM renderer uses the scope to pass down information about whether we’re currently rendering in an SVG element.
-- `TRoot` is the type of the root node. This is the type of the second parameter passed to the `Renderer.render` method, and the `root` prop passed to `Portal` elements. It is usually the same type as `TNode` but can vary according to renderer requirements.
-- `TResult` describes the type of values made visible to renderer consumers. Any time Crank exposes an internal node, for instance, via the `crank-ref` callback, or as the result of yield expressions in generator components, the renderer can intercept this access and provide something other than the internal nodes, allowing renderers to hide implementation details and provide results which make more sense for a specific environment.
- For example, the HTML string renderer has an internal node representation, but converts these nodes to strings before they’re exposed to consumers. This is done because the internal nodes must be referentially unique and mutated during rendering, while JavaScript strings are referentially transparent and immutable. Therefore, the `TResult` of the `HTMLRenderer` subclass is `string`.
+### `TNode`
+
+`TNode` is the most important type: it is the type of the node associated with each host element. For instance, for the basic DOM renderer, TNode is the [DOM Node interface](https://developer.mozilla.org/en-US/docs/Web/API/Node).
+
+### `TScope`
+
+`TScope` is the type of the *scope*, a renderer-specific concept for arbitrary data which is passed down the tree between host elements. Scopes are useful for passing contextual information down the tree to be used when nodes are created; for instance, the DOM renderer uses the scope to pass down information about whether we’re currently rendering in an SVG element.
+
+### `TRoot`
+`TRoot` is the type of the root node. This is the type of the second parameter passed to the `Renderer.render` method, and the `root` prop passed to `Portal` elements. It is usually the same type as `TNode` but can vary according to renderer requirements.
+
+### `TResult`
+
+`TResult` describes the type of values made visible to renderer consumers. Any time Crank exposes an internal node, for instance, via the `crank-ref` callback, or as the result of yield expressions in generator components, the renderer can intercept this access and provide something other than the internal nodes, allowing renderers to hide implementation details and provide results which make more sense for a specific environment.
+
+For example, the HTML string renderer has an internal node representation, but converts these nodes to strings before they’re exposed to consumers. This is because the internal nodes must be a referentially unique object which is mutated during rendering, while JavaScript strings are referentially transparent and immutable. Therefore, the `TResult` type of the HTML renderer is `string`.
## Methods
The following is a description of the signatures of internal renderer methods and when they’re executed.
@@ -42,51 +53,75 @@ The following is a description of the signatures of internal renderer methods an
```ts
create(
- tag: string | symbol, props: Record, scope: TScope
+ el: Element, scope: TScope
): TNode;
```
-The `create` method is called for each host element the first time the element is committed. The tag and props parameters are the tag and props of the host element which initiated this call, and the scope is the current scope of the element. The return value is the node which will be associated with the host element.
+The `create` method is called for each host element the first time the element is committed. This method is passed the current host element and scope, and should return the node which will be associated with the host element. This node will remain constant for an element for the duration that the element is mounted in the tree.
By default, this method will throw a `Not Implemented` error, so custom renderers should always implement this method.
+### Renderer.prototype.read
+```ts
+read(value: Array | TNode | string | undefined): TResult;
+```
+
+The renderer exposes rendered values in the following places:
+
+- As the return value of the renderer’s `render` method.
+- As the return value of the context’s `refresh` method.
+- As the argument passed to `crank-ref` props
+- As the argument passed to the context’s `schedule` and `cleanup` callbacks
+- Via the context’s `value` getter method
+- As the yield value of generator components
+
+When an element or elements are read in this way, we call the `read` method to give renderers a chance to manipulate what is exposed, so as to hide internal implementation details and return something which makes sense for the target environment. The parameter passed to read can be a node, a string, an array of nodes and strings, or undefined. The return value is what is actually exposed.
+
+This method is optional. By default, read is an identity function which returns the value passed in.
+
### Renderer.prototype.patch
```ts
patch(
- tag: string | symbol, props: Record, node: TNode, scope: TScope,
+ el: Element, node: TNode,
): unknown;
```
-The `patch` method is called for each host element whenever it is committed. The tag and props are the tag and props of the associated host element, the node is the value produced by the `create` method when the value was mounted, and the scope is the current scope of the element.
+The `patch` method is called for each host element whenever it is committed. This method is passed the current host element and its related node, and its return value is ignored. This method is usually where you would mutate the properties of the internal node according to the props of the host element.
-This method is useful for mutating nodes whenever the host element is committed. Implementation is optional and its return value is ignored.
+Implementation of this method is optional for renderers.
### Renderer.prototype.arrange
```ts
arrange(
- tag: string | symbol, props: Record, parent: TNode | TRoot, children: Array
+ el: Element,
+ parent: TNode | TRoot,
+ children: Array,
): unknown;
```
-The `arrange` method is called whenever an element’s children have changed. The tag and props are the tag and props of the associated host element, the parent is the value created by the create method for a host node and the `children` are the child values of all the element’s direct children. The `arrange` is also called for every root/portal element, so the parent can be of type `TRoot` as well as `TNode`.
+The `arrange` method is called whenever an element’s children have changed. It is called with the current host element, the host element’s related node, and the rendered values of all the element’s descendants as an array. In addition to when a host element commits, the `arrange` method may also be called when a child refreshes or otherwise causes the host element’s rendered children to change. Because the `arrange` method is called for every root/portal element, the parent can be of type `TRoot` as well as `TNode`.
-This method is where the magic happens, and is useful for connecting the nodes of your target environment as an internal tree.
+This method is where the magic happens, and is where you connect the nodes of your target environment as an internal tree.
### Renderer.prototype.scope
```ts
scope(
- tag: string | symbol, props: Record, scope: TScope | undefined
+ el: Element, scope: TScope | undefined
): TScope;
```
-The `scope` method is called for each host or portal element as elements are mounted or updated. Unlike the other custom renderer methods, the `scope` method is called during the pre-order traversal of the tree, much as components are. The `scope` method is passed the tag and props of the relevant host element, as well as the current scope, and the return value becomes the scope argument passed to the `create` and `scope` method calls for child host elements.
+The `scope` method is called for each host or portal element as elements are mounted or updated. Unlike the other custom renderer methods, the `scope` method is called during the pre-order traversal of the tree, much as components are. The `scope` method is passed the current host element and scope, and the return value becomes the scope argument passed to the `create` and `scope` method calls for child host elements.
+
+By default, the scope method returns `undefined`, meaning the scope will be `undefined` throughout your application.
### Renderer.prototype.escape
```ts
escape(text: string, scope: TScope): string;
```
-The `escape` method is called whenever a string is encountered in the element tree. It is mainly useful when creating string-based renderers like HTML or XML renderers, because most rendering targets like the DOM provide text node interfaces which sanitize inputs by default. One important detail is that `escape` should not return text nodes or anything besides a string. We defer this step to the `arrange` method because this allows the renderer to normalize a host element’s children by concatenating adjacent strings before it is passed to `arrange`.
+The `escape` method is called whenever a string is encountered in the element tree. It is mainly useful when creating string-based renderers like an HTML or XML renderer, because most rendering targets like the DOM provide text node interfaces which sanitize inputs by default.
+
+One important detail is that `escape` should not return text nodes or anything besides a string. We defer this step to the `arrange` method because this allows the renderer to normalize a host element’s children by concatenating adjacent strings.
By default, the `escape` method returns the string which was passed in.
@@ -95,18 +130,16 @@ By default, the `escape` method returns the string which was passed in.
parse(text: string, scope: TScope): TNode | string;
```
-When a `Raw` element is committed, if its `value` prop is a string, we call the `parse` method with that string and the current scope. The return value is the parsed node, or it can be a string as well, in which case parse will be handled like a string child by parents. The `escape` method will not be called on the return value.
+When the renderer encounters a `Raw` element whose `value` prop is a string, it calls the `parse` method with that string and the current scope. The return value should be the parsed node, or it can be a string as well, in which case parse will be handled like a string child by parents.
By default, the `parse` method returns the string which was passed in.
### Renderer.prototype.dispose
```ts
-dispose(
- tag: string | symbol, props: Record, node: TNode
-): unknown
+dispose(el: Element, node: TNode): unknown
```
-When a host element is unmounted, we call the `dispose` method with the related host element’s tag, props and node. This method is useful if you need to manually release a node or clean up event listeners for garbage collection purposes.
+The `dispose` method is called whenever a host element is unmounted. It is called with the host element and its related node. You can use this method to manually release a node or clean up event listeners for garbage collection purposes.
This method is optional and its return value is ignored.
@@ -115,24 +148,6 @@ This method is optional and its return value is ignored.
complete(root: TRoot): unknown;
```
-The `complete` method is called at the end of every render execution, when all elements have been committed and all other renderer methods have been called. It is useful, if your rendering target needs to be manually rerendered before any mutations take effect.
+The `complete` method is called at the end of every render execution, when all elements have been committed and all other renderer methods have been called. It is useful, if your rendering target needs some final render method to be executed before any mutations take effect.
This method is optional and its return value is ignored.
-
-### Renderer.prototype.read
-```ts
-read(value: Array | TNode | string | undefined): TResult;
-```
-
-The renderer exposes rendered values in the following places:
-
-- As the return value of `Renderer.prototype.render`
-- As the return value of `Context.prototype.refresh`
-- As the argument passed to `crank-ref` props
-- As the argument passed to `Context.prototype.schedule` and `Context.prototype.cleanup`
-- Via the `Context.prototype.value` getter method
-- As the yield value of generator components
-
-When an element or elements are read in this way, we call the `read` method to give renderers a chance to manipulate what is exposed, so as to hide internal implementation details and return something which makes sense for the target environment. The parameter passed to read can be a node, a string, an array of nodes and strings, or undefined. The return value is what is actually exposed.
-
-This method is optional. By default, read is an identity function which returns the value passed in.
diff --git a/website/guides/11-reference-for-react-developers.md b/website/guides/11-reference-for-react-developers.md
index 4414d8c06..9f2e3634c 100644
--- a/website/guides/11-reference-for-react-developers.md
+++ b/website/guides/11-reference-for-react-developers.md
@@ -38,7 +38,7 @@ The following are specific equivalents for React methods.
### `setState` and `forceUpdate`
Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](./guides/components#stateful-components).
-Crank is not “reactive” in the same sense as React, in that it does not actually track your component’s local state. You can either use `Context.prototype.refresh` to manually refresh the component, much like React’s `forceUpdate` method, or you can use async generator components, which refresh automatically whenever the returned async generator yields.
+Crank is not “reactive” in the same sense as React, in that it does not track your component’s local state and rerender when it detects a change. You can either use the context’s `refresh` to manually refresh the component, similar to React’s `forceUpdate` method, or you can use async generator components, which refresh automatically whenever the returned async generator yields.
### `defaultProps`
Crank doesn’t have a `defaultProps` implementation. Instead, you can provide default values when destructuring props. [See the guide on default props](./components#default-props).
From 8a94986714d4cbb82ed3ccf35b4698bc395e4af5 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 21 Jul 2020 11:27:38 -0400
Subject: [PATCH 21/26] tweaks to docs
---
website/guides/12-api-reference.md | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/website/guides/12-api-reference.md b/website/guides/12-api-reference.md
index ceed22f2b..3ec96469a 100644
--- a/website/guides/12-api-reference.md
+++ b/website/guides/12-api-reference.md
@@ -228,13 +228,7 @@ Constructs an element. Typicially, you would use the `createElement` function ra
An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
-**NOTE:** The internal Crank renderer methods documented in the [guide on custom renderers.](./custom-renderers)
-
-**Type Parameters:**
-- `TNode`
-- `TScope`
-- `TRoot`
-- `TResult`
+**NOTE:** The internal Crank renderer methods and type parameters are documented in the [guide on custom renderers.](./custom-renderers)
#### Methods
#### `constructor`
@@ -294,6 +288,7 @@ Adds an event listener to the context. The `addEventListener` method will also a
- `type: string` - The type of the event.
- `listener: Function` - The listener callback.
- `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
+
- `capture: boolean` - If true, the event listener will fire during [the capture phase](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) of event dispatch.
- `once: boolean` - If true, the event listener will fire at most once before being removed.
- `passive: boolean` - If true, calling the `preventDefault` method on the event will have no effect. Using this flag can increase performance. It is most useful for event types like `scroll` which fire frequently and are rarely cancelled.
@@ -306,6 +301,7 @@ Removes an event listener by type, listener and capture option.
- `type: string` - The type of the event.
- `listener: Function` - The listener callback.
- `options: Object | boolean` - An object of boolean options which can be set to change the characteristics of the listener. If the options object is a boolean, then that value is used to set the `capture` option for the listener.
+
- `capture: boolean` - If true, will remove listeners which use the capture option.
#### `dispatchEvent`
From 100c8ddd52df7f38266f9ce387e43033decca42a Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 28 Jul 2020 02:00:54 -0400
Subject: [PATCH 22/26] final pass
---
website/guides/02-elements.md | 8 +--
website/guides/03-components.md | 55 ++++++++++++-------
website/guides/04-handling-events.md | 24 ++++----
website/guides/05-async-components.md | 32 ++++++-----
website/guides/06-special-props-and-tags.md | 2 +-
website/guides/07-lifecycles.md | 30 +++++-----
website/guides/08-reusable-logic.md | 14 ++---
website/guides/10-custom-renderers.md | 52 ++++++++++--------
.../11-reference-for-react-developers.md | 41 +++++++-------
9 files changed, 140 insertions(+), 118 deletions(-)
diff --git a/website/guides/02-elements.md b/website/guides/02-elements.md
index 6377f0a30..604ef8211 100644
--- a/website/guides/02-elements.md
+++ b/website/guides/02-elements.md
@@ -18,7 +18,7 @@ const el =
An element
;
const el1 = createElement("div", {id: "element"}, "An element");
```
-The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, Crank provides special classes called *renderers* which interpret elements to produce DOM nodes, HTML strings, WebGL scene graphs, or whatever else you can think of.
+The `createElement` function provided by Crank returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, we use special classes called *renderers* to interpret elements and produce DOM nodes, HTML strings, WebGL-backed scene graphs, or whatever else you can think of.
Crank ships with two renderer subclasses for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
@@ -68,7 +68,7 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```
-We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how we “pass” arguments into functions when invoking them.
+We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you “pass” arguments into functions when invoking them.
If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).
@@ -107,7 +107,7 @@ renderer.render(el, document.body);
console.log(document.body.innerHTML); //
a2
```
-Crank also allows arbitrarily nested iterables of values to be interpolated as children, so, for instance, you can insert an array or a set of values into element trees.
+Crank also allows arbitrarily nested iterables of values to be interpolated as children, so, for instance, you can insert arrays or sets of elements into element trees.
```jsx
const arr = [1, 2, 3];
@@ -141,4 +141,4 @@ console.log(document.body.firstChild === div); // true
console.log(document.body.firstChild.firstChild === span); // true
```
-**Note:** We usually avoid using the term “virtual DOM” in Crank, insofar as the core renderer can be extended to target multiple environments; instead, we use the term “element diffing” to mean mostly the same thing.
+**Note:** The documentation avoids the terms “virtual DOM” or “DOM diffing” insofar as the core renderer can be extended to target multiple environments; instead, we use the terms “virtual elements” and “element diffing” to mean mostly the same thing.
diff --git a/website/guides/03-components.md b/website/guides/03-components.md
index 3a786d75e..6a58a4b24 100644
--- a/website/guides/03-components.md
+++ b/website/guides/03-components.md
@@ -13,7 +13,8 @@ function Greeting({name}) {
}
renderer.render(, document.body);
-console.log(document.body.innerHTML); // "
Hello World
"
+console.log(document.body.innerHTML);
+// "
Hello World
"
```
Component elements can be passed children just as host elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place these children somewhere in the returned element tree. If you don’t use the `children` prop, it will not appear in the rendered output.
@@ -34,7 +35,8 @@ renderer.render(
document.body,
);
-console.log(document.body.innerHTML); // "
"
```
-By yielding elements rather than returning them, we can make components stateful using variables in the generator’s local scope. Every time a generator component is rendered, Crank resumes the generator, pausing at the next `yield`. The yielded expression, usually an element, is then recursively rendered, just as if it were returned from a function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of generator components are preserved between renders.
+By yielding elements rather than returning them, we can make components stateful using variables in the generator’s local scope. Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that their executions are preserved between renders. Every time a generator component is rendered, Crank resumes the generator and executes the generator until the next `yield`. The yielded expression, usually an element, is then rendered as the element’s children, just as if it were returned from a function component.
### Contexts
-In the preceding example, the `Counter` component’s local state changes when it is rerendered, but we may want to write components which update themselves instead according to timers or events. Crank allows components to control themselves by passing in an object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component instance in place.
+In the preceding example, the `Counter` component’s local state changed when it was rerendered, but we may want to write components which update themselves according to timers or events instead. Crank allows components to control their own execution by passing in an object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component instance in place.
```jsx
function *Timer() {
@@ -99,10 +101,10 @@ function *Timer() {
This `Timer` component is similar to the `Counter` one, except now the state (the local variable `seconds`) is updated in the callback passed to `setInterval`, rather than when the component is rerendered. Additionally, the `refresh` method is called to ensure that the generator is stepped through whenever the `setInterval` callback fires, so that the rendered DOM actually reflects the updated `seconds` variable.
-One important detail about the `Timer` example is that it cleans up after itself with `clearInterval`. Crank will call the `return` method on generator components when the element is unmounted, so that the finally block executes and `clearInterval` is called. In this way, you can use the natural lifecycle of a generator to write setup and teardown logic for components, all within the same scope.
+One important detail about the `Timer` example is that it cleans up after itself with `clearInterval` in the `finally` block. Crank will call the `return` method on an element’s related generator object when it is unmounted.
### Props Updates
-The generator components we’ve seen so far haven’t used props. Generator components can accept props as its first parameter just like regular function components.
+The generator components we’ve seen so far haven’t used props. Generator components can accept props as their first parameter just like regular function components.
```jsx
function *LabeledCounter({message}) {
@@ -113,13 +115,22 @@ function *LabeledCounter({message}) {
}
}
-renderer.render(, document.body);
+renderer.render(
+ ,
+ document.body,
+);
+
console.log(document.body.innerHTML); // "
"
```
-This mostly works, except now we have a bug where the component kept yielding the initial message even though a new message was passed in via props. To fix this, we can make sure props are kept up to date by iterating over the context:
+This mostly works, except we have a bug where the component keeps yielding elements with the initial message even though a new message was passed in via props. We can make sure props are kept up to date by iterating over the context:
```jsx
function *Counter({message}) {
@@ -140,21 +151,26 @@ function *Counter({message}) {
}
}
-renderer.render(, document.body);
+renderer.render(
+ ,
+ document.body,
+);
+
console.log(document.body.innerHTML); // "
"
```
-By replacing the `while` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to elements.
+By replacing the `while` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to components.
### Comparing Old and New Props
-One idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters with the destructuring expression in the `for…of` statement. This is an easy way to make sure those variables stay in sync with the current props of the component. However, there is no requirement that you must always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
+One Crank idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters with the destructuring expression in the `for…of` statement. This is an easy way to make sure those variables stay in sync with the current props of the component. However, there is no requirement that you must always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old props.
```jsx
function *Greeting({name}) {
@@ -185,7 +201,7 @@ console.log(document.body.innerHTML); // "
Hello again Bob
"
The fact that state is just local variables allows us to blur the lines between props and state, in a way that is easy to understand and without lifecycle methods like `componentWillUpdate` from React. With generators and `for` loops, comparing old and new props is as easy as comparing adjacent elements of an array.
## Default Props
-You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to a specific prop by using JavaScript’s default value syntax.
+You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to specific props by using JavaScript’s default value syntax.
```jsx
function Greeting({name="World"}) {
@@ -195,12 +211,13 @@ function Greeting({name="World"}) {
renderer.render(, document.body); // "
Hello World
"
```
-This works well for function components, but for generator components, you should make sure that you use the same default value in both the parameter list and the `for` statement.
+This syntax works well for function components, but for generator components, you should make sure that you use the same default value in both the parameter list and the `for` statement.
```jsx
function *Greeting({name="World"}) {
+ yield
Hello, {name}
;
for ({name="World"} of this) {
- yield
Hello, {name}
;
+ yield
Hello again, {name}
;
}
}
```
diff --git a/website/guides/04-handling-events.md b/website/guides/04-handling-events.md
index 39b3246b5..0d63dc3d1 100644
--- a/website/guides/04-handling-events.md
+++ b/website/guides/04-handling-events.md
@@ -2,7 +2,7 @@
title: Handling Events
---
-Most web applications require some measure of interactivity, where the user interface updates according to user input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
+Most web applications require some measure of interactivity, where the user interface updates according to input. To facilitate this, Crank provides two APIs for listening to events on rendered DOM nodes.
## DOM onevent Props
You can attach event callbacks to host element directly using onevent props. These props start with `on`, are all lowercase, and correspond to the properties as specified according to the DOM’s [GlobalEventHandlers mixin API](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers). By combining event props, local variables and `this.refresh`, you can write interactive components.
@@ -49,9 +49,7 @@ The local state `count` is now updated in the event listener, which triggers whe
**NOTE:** When using the context’s `addEventListener` method, you do not have to call the `removeEventListener` method if you merely want to remove event listeners when the component is unmounted. This is done automatically.
-## Event Delegation
-
-The context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation:
+The context’s `addEventListener` method only attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation.
```jsx
function *Clicker() {
@@ -77,14 +75,14 @@ function *Clicker() {
Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button` element.
## onevent vs EventTarget
-The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target, and you can register them on exactly the element you’d like to listen to.
+The props-based onevent API and the context-based EventTarget API both have their advantages. On the one hand, using onevent props means you don’t have to filter events by target. You register them on exactly the element you’d like to listen to.
-On the other, using the `addEventListener` method allows you to take full advantage of the EventTarget API, including registering passive event listeners or events which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components whose children are passed in, or in utility functions which don’t have access to produced elements.
+On the other, using the `addEventListener` method allows you to take full advantage of the EventTarget API, which includes registering passive event listeners or listeners which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to components which are passed children, or in utility functions which don’t have access to produced elements.
Crank supports both API styles for convenience and flexibility.
-## Dispatching events
-Crank contexts implement the full EventTarget interface, meaning you can use the `dispatchEvent` method and the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) class to dispatch events to ancestor components:
+## Dispatching Events
+Crank contexts implement the full EventTarget interface, meaning you can use [the `dispatchEvent` method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) and [the `CustomEvent` class](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) to dispatch custom events to ancestor components:
```jsx
function MyButton(props) {
@@ -132,11 +130,11 @@ The preceding example also demonstrates a slight difference in the way the `addE
## Form Elements
-Form elements like inputs and textareas are stateful and by default update themselves automatically according to user input. JSX libraries like React and Inferno handle these types of elements by allowing their virtual representations to be “controlled” or “uncontrolled,” where being controlled means that the internal DOM node’s state is synced to the virtual representation’s props.
+Form elements like inputs and textareas are stateful and by default update themselves automatically according to user input. JSX libraries like React and Inferno handle these types of elements by allowing their virtual representations to be “controlled” or “uncontrolled,” where being controlled means that the internal DOM node’s state is synced to the virtual representation’s props. These APIs manifest as special “uncontrolled” versions of props like `defaultValue` and `defaultChecked`.
-Crank’s philosophy with regard to this issue is slightly different, in that we do not view the virtual elements as the “source of truth” for the underlying DOM node. In practice, this design decision means that renderers do not retain the previously rendered props for host elements. For instance, Crank will not compare old and new props between renders to avoid mutating props which have not changed, and instead attempt to update every prop in props when it is rerendered.
+Crank’s approach to this issue is slightly different, in that we do not view the virtual elements as the “source of truth” for the underlying DOM nodes. In practice, this design decision means that renderers do not retain the previously rendered props for host elements. For instance, Crank will not compare old and new props between renders to avoid mutating props which have not changed, and instead attempt to update every prop found in props.
-Another consequence is that we don’t delete props which were present in one rendering and absent in the next. In the following example, the checkbox will not be unchecked if you press the button.
+Another consequence is that we don’t delete props which were present in one rendering and absent in the next. In the following example, the checkbox will never uncheck itself.
```jsx
function *App() {
@@ -195,7 +193,7 @@ function *App() {
}
```
-This design decision means that we now have a way to have form elements be “uncontrolled” and “controlled” within the same component. Here, for instance, is an input element which is uncontrolled, except that it resets when the button is clicked.
+This design decision means that we now have a way to make the same element prop both “uncontrolled” and “controlled” for an element. Here, for instance, is an input element which is uncontrolled, except that it resets when the button is clicked.
```jsx
function* ResettingInput() {
@@ -220,4 +218,4 @@ function* ResettingInput() {
}
```
-We use the `reset` flag to check whether we need to set the `value` of the underlying input DOM element, and we omit the `value` prop when we aren’t performing a reset. The input is therefore both uncontrolled in that we do not track its value, but also controllable via normal rendering when we want to reset its `value` to the empty string.
+In the above example, we use the `reset` flag to check whether we need to set the `value` prop of the underlying input DOM element, and we omit the `value` prop when we aren’t performing a reset. Because the prop is not cleared when absent from the virtual element’s props, Crank leaves it alone. Crank’s approach means we do not need special alternative props for uncontrolled behavior, and we can continue to use virtual element rendering over raw DOM mutations in those circumstances where we do need control.
diff --git a/website/guides/05-async-components.md b/website/guides/05-async-components.md
index 4eda5f731..da6247e78 100644
--- a/website/guides/05-async-components.md
+++ b/website/guides/05-async-components.md
@@ -2,7 +2,7 @@
title: Async Components
---
-## Async function components
+## Async Function Components
So far, every component we’ve seen has worked synchronously, and Crank will respect this as an intentional decision by the developer by keeping the entire process of rendering synchronous from start to finish. However, modern JavaScript includes promises and `async`/`await`, which allow you to write concurrently executing code as if it were synchronous. To facilitate these features, Crank allows components to be asynchronous functions as well, and we call these components, *async function components*.
```jsx
@@ -18,9 +18,9 @@ async function IPAddress () {
})();
```
-When a Crank renderer runs a component which returns a promise, the rendering process becomes asynchronous as well. Concretely, this means that `render` or `refresh` calls which render async compenents return a promise which fulfills when all async components in the tree have fulfilled. It also means that no actual DOM updates will be triggered until that moment.
+When Crank renders an async component anywhere in the tree, the entire process becomes asynchronous. Concretely, this means that `renderer.render` or `this.refresh` calls return a promise which fulfills when rendering has finished. It also means that no actual DOM updates will be triggered until this moment.
-### Concurrent updates
+### Concurrent Updates
Because async function components can be rerendered while they are still pending, Crank implements a couple rules to make concurrent updates predictable and performant:
1. There can only be one pending run of an async function component at the same time for an element in the tree. If the same async component is rerendered concurrently while it is still pending, another call is enqueued with the latest props.
@@ -35,23 +35,23 @@ async function Delay ({message}) {
const p1 = renderer.render(, document.body);
console.log(document.body.innerHTML); // ""
await p1;
- console.log(document.body.innerHTML); // "Run 1"
+ console.log(document.body.innerHTML); // "
Run 1
"
const p2 = renderer.render(, document.body);
// These renders are enqueued because the second render is still pending.
const p3 = renderer.render(, document.body);
const p4 = renderer.render(, document.body);
- console.log(document.body.innerHTML); // "Run 1"
+ console.log(document.body.innerHTML); // "
"
// By the time the third render fulfills, the fourth render has already completed.
await p3;
- console.log(document.body.innerHTML); // "Run 4"
+ console.log(document.body.innerHTML); // "
"
})();
```
-In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings are enqueued, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then, only the fourth rendering is actually executed because third rendering’s props are obsolete by the time the component is ready to update again. This behavior allows async components to always be kept up-to-date without producing excess calls to async code.
+In the preceding example, at no point is there more than one simultaneous call to the `Delay` component, despite the fact that it is rerendered concurrently for its second through fourth renders. And because these renderings are enqueued, only the second and fourth renderings have any effect. This is because the element is busy with the second render by the time the third and fourth renderings are requested, and then, only the fourth rendering is actually executed because third rendering’s props are obsolete by the time the component is ready to update again. This behavior allows async components to always be kept up-to-date without producing excess calls to the function.
2. If two different async components are rendered in the same position, the components are raced. If the earlier component fulfills first, it shows until the later component fulfills. If the later component fulfills first, the earlier component is never rendered.
@@ -97,7 +97,7 @@ TODO: this section is too hard to understand and requires code examples so we’
When Crank encounters an async component anywhere in the element tree, the entire rendering process becomes asynchronous. Therefore, async child components make parent components asynchronous, and sync function and generator components behave differently when they produce async children. On the one hand, sync function components transparently pass updates along to async children, so that when a renderer updates a sync function component concurrently, its async children will also enqueue an update immediately. On the other hand, sync generator components which produce async elements will not resume until those async children have fulfilled. This is because sync generators expect to be resumed after their children have rendered, and the actual DOM nodes which are created are passed back into the generator, but they wouldn’t be available if the generator was concurrently resumed before the async children had settled.
-->
-## Async generator components
+## Async Generator Components
Just as you can write stateful components with sync generator functions, you can also write stateful *async* components with *async generator functions*.
```jsx
@@ -131,10 +131,14 @@ async function *AsyncLabeledCounter ({message}) {
})();
```
-`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in [the section on props updates](./components#props-updates). This example demonstrates several key differences between sync and async generator components. Firstly, rather than using `while` or `for…of` loops as with sync generator components, we now use a `for await…of` loop. This is possible because contexts are not just an *iterable* of props, but also an *async iterable* of props as well. Secondly, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators components to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed. Rather than suspending at each yield, we rely on the `for await…of` loop, which suspends at the bottom until the next update.
+`AsyncLabeledCounter` is an async version of the `LabeledCounter` example introduced in [the section on props updates](./components#props-updates). This example demonstrates several key differences between sync and async generator components. Firstly, rather than using `while` or `for…of` loops as with sync generator components, we now use [a `for await…of` loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of). This is possible because contexts are not just an *iterable* of props, but also an *async iterable* of props as well.
-## Responsive Loading Indicators
-The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all components in the tree have fulfilled. This can be a problem when you have an async call which takes a long time. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences. However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we can use the async rules described previously along with async generator functions to show loading indicators which appear only when certain promises take too long to settle.
+Secondly, you’ll notice that the async generator yields multiple times per iteration over `this`, once to show a loading message and once to show the actual count. While it is possible for sync generators components to yield multiple times per iteration over `this`, it wouldn’t necessarily make sense to do so because generators suspend at each yield, and upon resuming a second time within the same loop, the props would be stale. In contrast, async generator components are continuously resumed. Rather than suspending at each yield, we rely on the `for await…of` loop, which suspends at its end until the next update.
+
+### Loading Indicators
+The async components we’ve seen so far have been all or nothing, in the sense that Crank can’t show anything until all promises in the tree have fulfilled. This can be a problem when you have an async call which takes longer than expected. It would be nice if parts of the element tree could be shown without waiting, to create responsive user experiences.
+
+However, because loading indicators which show immediately can paradoxically make your app seem less responsive, we use the async rules described previously along with async generator functions to show loading indicators which appear only when certain components take too long.
```jsx
async function LoadingIndicator() {
@@ -214,4 +218,4 @@ async function *Suspense({timeout, fallback, children}) {
})();
```
-No special tags are needed for async loading states, and the functionality to write this complex logic is implemented using the same element diffing algorithm that governs synchronous components. Additionally, this approach is more flexible in the sense that you can extend it; for instance, you can add another yield to the `for await…of` loop to show a second fallback state which waits ten seconds, to inform the user that something went wrong or that servers are slow to respond.
+No special tags are needed for async loading states, and the functionality to write this logic is implemented using the same element diffing algorithm that governs synchronous components. Additionally, this approach is more flexible in the sense that you can extend it; for instance, you can add another yield to the `for await…of` loop to show a second fallback state which waits ten seconds, to inform the user that something went wrong or that servers are slow to respond.
diff --git a/website/guides/06-special-props-and-tags.md b/website/guides/06-special-props-and-tags.md
index c6dca80be..c00e16fd3 100644
--- a/website/guides/06-special-props-and-tags.md
+++ b/website/guides/06-special-props-and-tags.md
@@ -2,7 +2,7 @@
title: Special Props and Tags
---
-Crank provides certain APIs in the form of special props or element tags.
+Crank provides certain APIs in the form of special props or element tags. The following is an overview of these props and tags.
## Special Props
The following props apply to all elements, regardless of tag or renderer.
diff --git a/website/guides/07-lifecycles.md b/website/guides/07-lifecycles.md
index e0725ae83..430d7f139 100644
--- a/website/guides/07-lifecycles.md
+++ b/website/guides/07-lifecycles.md
@@ -4,9 +4,9 @@ title: Lifecycles
Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the returned generator object as components are mounted, updated and unmounted from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.
-## Returning
+## Returning Values
-Usually, you’ll yield in generator components so that they can continue to respond to updates, but you may want to also `return` a final state. Unlike function components, which are called and returned once for each update, once a generator component returns, it will never update again.
+In most generator components, you will yield children within a loop so that they can continue to respond to updates. However, you may also want to return a final state. Unlike function components, which are called and returned once for each update, once a generator component returns, its rendered value is final, and the component will never update again.
```jsx
function *Stuck({message}) {
@@ -21,7 +21,7 @@ renderer.render(, document.bo
console.log(document.body.innerHTML); // "
Hello
"
```
-You should be careful when writing generator components to make sure that you always place your `yield` operators in a `for` or `while` loop. If you forget and implicitly return from the generator, it will stop updating, nothing will be rendered, and the only way to restart the component will be to unmount and remount the component into the element tree.
+You should be careful when writing generator components to make sure that you always place your `yield` operators in a `for` or `while` loop. If you forget and implicitly return from the generator, it will stop updating and nothing will be rendered ever again.
```jsx
function *Numbers() {
@@ -40,15 +40,11 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // ""
renderer.render(, document.body);
console.log(document.body.innerHTML); // ""
-
-renderer.render(null, document.body);
-renderer.render(, document.body);
-console.log(document.body.innerHTML); // "1"
```
## Cleaning Up
-When a generator component is removed from the tree, Crank calls the `return` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator suspended are broken out of, and code after the yield does not execute.
+When a generator component is removed from the tree, Crank calls the `return` method on the component’s generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator suspended are broken out of, and code after the yield does not execute.
You can take advantage of this behavior by wrapping your `yield` loops in a `try`/`finally` block to release any resources that your component may have used.
@@ -70,10 +66,10 @@ renderer.render(null, document.body);
console.log(document.body); // ""
```
-[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to `try`/`finally` blocks in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the produced values and doing so might cause your components to inadvertently swallow errors or suspend in an unexpected location.
+[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to `try`/`finally` statements in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the yielded or returned values and doing so might cause your components to inadvertently swallow errors or suspend in unexpected locations.
## Catching Errors
-We all make mistakes, and it can be useful to catch errors in our components so that we can show the user something or notify error-logging services. To facilitate this, Crank will catch errors thrown when rendering child elements and throw them back into parent generator components by calling the `throw` method on the generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced with a `throw` statement with the error set to whatever was thrown by the component’s children.
+We all make mistakes, and it can be useful to catch errors thrown by our components so that we can show the user something or notify error-logging services. To facilitate this, Crank will catch errors thrown when rendering child elements and throw them back into parent generator components using the `throw` method on the component’s generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced with a `throw` statement with the error set to whatever was thrown by the component’s children.
You can take advantage of this behavior by wrapping your `yield` operations in a `try`/`catch` block to catch errors caused by children.
@@ -98,7 +94,7 @@ renderer.render(, document.body);
console.log(document.body.innerHTML); // "
Error: Hmmm
"
```
-This component “sticks” at the return so that the same error message is shown until the component is unmounted. However, you may also want to recover from errors as well, and you can do this by ignoring or handling the error.
+As explained previously, this component “sticks” because it uses a return statement, so that the same error message is shown until the component is unmounted. However, you may also want to recover from errors as well, and you can do this by ignoring or handling the error.
```jsx
function T1000() {
@@ -117,13 +113,17 @@ function *Terminator() {
}
renderer.render(, document.body);
-console.log(document.body.innerHTML); // "
"
```
## Accessing Rendered Values
diff --git a/website/guides/08-reusable-logic.md b/website/guides/08-reusable-logic.md
index 2c7c2562c..c734a9760 100644
--- a/website/guides/08-reusable-logic.md
+++ b/website/guides/08-reusable-logic.md
@@ -10,12 +10,12 @@ Crank provides several additional methods and properties via the `Context` API t
### `context.props`
The current props of a component can be accessed via the readonly context property `props`. We recommended that you access props within components via its parameters or context iterators when writing components. The `props` property can be useful when you need to access a component’s current props from within an extension or helper function.
-### `context.value`
+### `Context.prototype.value`
Similarly, the most recently rendered value of a component is accessible via the readonly context property `value`. Again, we recommend that you access rendered values via the many methods described in [the guide on accessing rendered values](./lifecycles#accessing-rendered-values) or via [the `crank-ref` prop](./special-props-and-tags#crank-ref), but it can be useful to access the current value synchronously when writing helper context methods.
Depending on the state of the component, the accessed value can be an node, a string, an array of nodes and strings, or `undefined`.
-### Provisions
+### `context.provide` and `context.consume`
**Warning:** This API is more unstable than others, and the method names and behavior of components which use this method may change.
Crank allows you to provide data to all of a component’s descendants via the methods `provide` and `consume`. The `provide` method sets a “provision” under a specific key, and the `consume` method retrieves the value set under a specific key by the nearest ancestor.
@@ -49,19 +49,17 @@ renderer.render(
console.log(document.body); // "
Hello, Brian
"
```
-Provisions allow libraries to define components which interact with their descendants without rigidly defined component hierarchies or requiring the developer to pass data manually between components as props. This makes them useful, for instance, when writing multiple components which communicate with each other, like custom `select` and `option` form elements, or drag-and-drop components.
+Provisions allow libraries to define components which interact with their descendants without rigidly defined component hierarchies or requiring data to be passed manually between components via props. This makes them useful, for instance, when writing multiple components which communicate with each other, like custom `select` and `option` form elements, or drag-and-drop components.
Anything can be passed as a key to the `provide` and `consume` methods, so you can use a symbol to ensure that the provision you pass between your components are private and do not collide with provisions set by others.
-**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when `provide` is called, so it’s up to you to make sure consumers update when providers update.
+**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when the `provide` method is called. It’s up to you to ensure consumers update when providers update.
### `context.schedule`
-You can pass a callback to the `schedule` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only parameter. They only fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
+You can pass a callback to the `schedule` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only argument. Scheduled callbacks fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
### `context.cleanup`
-Similarly, you can pass a callback to the `cleanup` method to listen for when the component unmounts.
-
-All `cleanup` callbacks fire synchronously when the component is removed, and only once per registered callback function. They are called iwth the rendered value of the component as its only parameter.
+Similarly, you can pass a callback to the `cleanup` method to listen for when the component unmounts. Callbacks passed to `cleanup` fire synchronously when the component is unmounted. Each registered callback fires only once. The callback is called with the last rendered value of the component as its only argument.
## Strategies for Reusing Logic
diff --git a/website/guides/10-custom-renderers.md b/website/guides/10-custom-renderers.md
index 37f9aa729..1f451a6dd 100644
--- a/website/guides/10-custom-renderers.md
+++ b/website/guides/10-custom-renderers.md
@@ -47,9 +47,9 @@ class Renderer<
For example, the HTML string renderer has an internal node representation, but converts these nodes to strings before they’re exposed to consumers. This is because the internal nodes must be a referentially unique object which is mutated during rendering, while JavaScript strings are referentially transparent and immutable. Therefore, the `TResult` type of the HTML renderer is `string`.
## Methods
-The following is a description of the signatures of internal renderer methods and when they’re executed.
+The following is a description of the signatures of internal renderer methods and when they’re executed. When creating a custom renderer, you are expected to override these methods via inheritance.
-### Renderer.prototype.create
+### `Renderer.prototype.create`
```ts
create(
@@ -57,11 +57,12 @@ create(
): TNode;
```
-The `create` method is called for each host element the first time the element is committed. This method is passed the current host element and scope, and should return the node which will be associated with the host element. This node will remain constant for an element for the duration that the element is mounted in the tree.
+The `create` method is called for each host element the first time the element is committed. This method is passed the current host element and scope, and should return the node which will be associated with the host element. This node will remain constant for an element for as long as the element is rendered.
By default, this method will throw a `Not Implemented` error, so custom renderers should always implement this method.
-### Renderer.prototype.read
+### `Renderer.prototype.read`
+
```ts
read(value: Array | TNode | string | undefined): TResult;
```
@@ -75,22 +76,22 @@ The renderer exposes rendered values in the following places:
- Via the context’s `value` getter method
- As the yield value of generator components
-When an element or elements are read in this way, we call the `read` method to give renderers a chance to manipulate what is exposed, so as to hide internal implementation details and return something which makes sense for the target environment. The parameter passed to read can be a node, a string, an array of nodes and strings, or undefined. The return value is what is actually exposed.
+When an element or elements are read in this way, we call the `read` method to give renderers a final chance to manipulate what is exposed, so as to hide internal implementation details and return something which makes sense for the target environment. The parameter passed to the `read` method can be a node, a string, an array of nodes and strings, or `undefined`. The return value is what is actually exposed.
This method is optional. By default, read is an identity function which returns the value passed in.
-### Renderer.prototype.patch
+### `Renderer.prototype.patch`
+
```ts
-patch(
- el: Element, node: TNode,
-): unknown;
+patch(el: Element, node: TNode): unknown;
```
-The `patch` method is called for each host element whenever it is committed. This method is passed the current host element and its related node, and its return value is ignored. This method is usually where you would mutate the properties of the internal node according to the props of the host element.
+The `patch` method is called for each host element whenever it is committed. This method is passed the current host element and its related node, and its return value is ignored. This method is usually where you would mutate the internal node according to the props of the host element.
Implementation of this method is optional for renderers.
-### Renderer.prototype.arrange
+### `Renderer.prototype.arrange`
+
```ts
arrange(
el: Element,
@@ -99,22 +100,22 @@ arrange(
): unknown;
```
-The `arrange` method is called whenever an element’s children have changed. It is called with the current host element, the host element’s related node, and the rendered values of all the element’s descendants as an array. In addition to when a host element commits, the `arrange` method may also be called when a child refreshes or otherwise causes the host element’s rendered children to change. Because the `arrange` method is called for every root/portal element, the parent can be of type `TRoot` as well as `TNode`.
+The `arrange` method is called whenever an element’s children have changed. It is called with the current host element, the host element’s related node, and the rendered values of all the element’s descendants as an array. In addition to when a host element commits, the `arrange` method may also be called when a child refreshes or otherwise causes a host element’s rendered children to change. Because the `arrange` method is called for every root/portal element, the parent can be of type `TRoot` as well as `TNode`.
+
+This method is where the magic happens, and is where you connect the nodes of your target environment into an internal tree.
-This method is where the magic happens, and is where you connect the nodes of your target environment as an internal tree.
+### `Renderer.prototype.scope`
-### Renderer.prototype.scope
```ts
-scope(
- el: Element, scope: TScope | undefined
-): TScope;
+scope(el: Element, scope: TScope | undefined): TScope;
```
-The `scope` method is called for each host or portal element as elements are mounted or updated. Unlike the other custom renderer methods, the `scope` method is called during the pre-order traversal of the tree, much as components are. The `scope` method is passed the current host element and scope, and the return value becomes the scope argument passed to the `create` and `scope` method calls for child host elements.
+The `scope` method is called for each host or portal element as elements are mounted or updated. Unlike the other custom renderer methods, the `scope` method is called during the pre-order traversal of the tree, much as components are. The `scope` method is passed the current host element and scope as parameters, and the return value becomes the scope argument passed to the `create` and `scope` method calls for descendant host elements.
-By default, the scope method returns `undefined`, meaning the scope will be `undefined` throughout your application.
+By default, the `scope` method returns `undefined`, meaning the scope will be `undefined` throughout your application.
+
+### `Renderer.prototype.escape`
-### Renderer.prototype.escape
```ts
escape(text: string, scope: TScope): string;
```
@@ -125,7 +126,8 @@ One important detail is that `escape` should not return text nodes or anything b
By default, the `escape` method returns the string which was passed in.
-### Renderer.prototype.parse
+### `Renderer.prototype.parse`
+
```ts
parse(text: string, scope: TScope): TNode | string;
```
@@ -134,7 +136,8 @@ When the renderer encounters a `Raw` element whose `value` prop is a string, it
By default, the `parse` method returns the string which was passed in.
-### Renderer.prototype.dispose
+### `Renderer.prototype.dispose`
+
```ts
dispose(el: Element, node: TNode): unknown
```
@@ -143,11 +146,12 @@ The `dispose` method is called whenever a host element is unmounted. It is calle
This method is optional and its return value is ignored.
-### Renderer.prototype.complete
+### `Renderer.prototype.complete`
+
```ts
complete(root: TRoot): unknown;
```
-The `complete` method is called at the end of every render execution, when all elements have been committed and all other renderer methods have been called. It is useful, if your rendering target needs some final render method to be executed before any mutations take effect.
+The `complete` method is called at the end of every render execution, when all elements have been committed and all other renderer methods have been called. It is useful, for instance, if your rendering target needs some final code to execute before any mutations take effect.
This method is optional and its return value is ignored.
diff --git a/website/guides/11-reference-for-react-developers.md b/website/guides/11-reference-for-react-developers.md
index 9f2e3634c..e2adaf006 100644
--- a/website/guides/11-reference-for-react-developers.md
+++ b/website/guides/11-reference-for-react-developers.md
@@ -31,7 +31,7 @@ async function *ReactComponent(props) {
}
```
-The example is pseudocode which demonstrates where React’s class methods would be called relative to an async generator component. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.
+This example is pseudocode which demonstrates where React’s class methods would be called relative to an async generator component. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.
The following are specific equivalents for React methods.
@@ -50,7 +50,7 @@ Setup code for components can be written at the top of generator components. It
As an alternative to React’s `shouldComponentUpdate` method, you can use `Copy` elements to prevent the rerendering of a specific subtree. Refer to [the description of `Copy` elements](./special-props-and-tags#copy) for more information.
### `getDerivedStateFromProps`, `componentWillUpdate` and `getSnapshotBeforeUpdate`
-Code which compares old and new props or state can be written directly in your components. See the section on [`prop updates`](./components#comparing-old-and-new-props) for an example of a component which compares old and new props.
+Code which compares old and new props or state can be written directly in your components. See the section on [prop updates](./components#comparing-old-and-new-props) for an example of a component which compares old and new props.
### `componentDidUpdate`
To execute code after rendering, you can use async generator components or [the `schedule` method](./api-reference#schedule). See [the guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
@@ -62,22 +62,21 @@ You can use a `try`/`finally` block to run code when a component is unmounted. Y
To catch errors which occur in child components, you can use generator components and wrap `yield` operations in a `try`/`catch` block. Refer to [the relevant guide on catching errors](./lifecycles#catching-errors).
## Hooks
-Crank does not implement any APIs similar to React Hooks. The following are alternatives to specific hooks.
+Crank does not implement any APIs similar to React Hooks. The main appeal of hooks for library authors is that you can encapsulate entire APIs in one or two hooks. Refer to [the guide on reusable logic](./reusable-logic#strategies-for-reusing-logic) for a description of strategies you can use to reuse logic and write library wrappers in Crank.
+
+The following are alternatives to specific hooks.
### `useState` and `useReducer`
Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](./components#stateful-components).
### `useEffect` and `useLayoutEffect`
-Crank does not have any requirements that rendering should be “pure.” In other words, you can trigger side-effects directly while rendering because Crank does not execute components more times than you might expect.
+Crank does not have any requirements that rendering should be “pure.” In other words, you can trigger side-effects directly while rendering because Crank does not execute components more times than you might expect. Refer to [the guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information on code which executes after rendering.
### `useMemo` and `useCallback`
Because the execution of generator components is preserved, there is no need to “memoize” or “cache” callbacks or other values. You can simply assign them to a constant variable.
### `useImperativeHandle`
-Crank does not have a way to access component instances, and parent components should not access child components directly. A wrapper which uses web components for the definition of custom elements with imperative methods and properties is planned.
-
-### Custom Hooks
-The main appeal of hooks for library authors is that you can encapsulate entire APIs in one or two hooks. Refer to [the guide on reusable logic](./reusable-logic#strategies-for-reusing-logic) for various patterns and strategies to wrap APIs and logic in Crank.
+Crank does not have a way to access component instances, and parent components should not access child components directly. A web component wrapper for defining custom elements with imperative APIs is planned.
## Suspense and Concurrent Mode
Crank uses async functions and promises for scheduling and coordinating async processes. See the [guide on async components](./async-components) for an introduction to async components, as well as a demonstration of how you can implement the `Suspense` component directly in user space.
@@ -89,7 +88,7 @@ Crank is written in TypeScript, and you can add type checking to components by t
Crank does not restrict children in JSX elements to just arrays. You can interpolate ES6 maps, sets or any other iterable into your Crank elements. Additionally, Crank does not warn you if elements in the iterable are unkeyed.
## Fragments
-The [`Fragment` element](./special-props-and-tags#fragment) works almost exactly the same as it does in React, except in Crank you can also use a callback ref to access its contents.
+The [`Fragment` element](./special-props-and-tags#fragment) works almost exactly as it does in React, except that in Crank you can also use a callback ref to access its contents.
## `React.cloneElement`
You can clone elements using [the `cloneElement` function](./api-reference#cloneelement).
@@ -98,45 +97,47 @@ You can clone elements using [the `cloneElement` function](./api-reference#clone
The `createPortal` function is replaced by the special `Portal` element, whose behavior and expected props varies according to the target rendering environment. Refer to [the guide on the `Portal` element](./special-props-and-tags#portal) for more information.
## `React.memo`
-See [the guide on `Copy` elements](./special-props-and-tags#copy) for a demonstration of how you can use the `Copy` tag to implement `React.memo` in user space.
+See [the guide on `Copy` tags](./special-props-and-tags#copy) for a demonstration of how you can use `Copy` elements to implement `React.memo` in user space.
## DOM element props
-The following are a list of the differences in APIs when passings props to DOM elements.
+The following are a list of the differences in props APIs for DOM elements.
### `className` and `htmlFor`
-We prefer attribute names rather than the JS property equivalents when the two diverge.
+Crank prefers attribute names rather than the JS property DOM equivalents when these names are mismatched.
```jsx
````
-See [the section on prop naming conventions](./special-props-and-tags#prop-naming-conventions) for more information.
+In short, Crank is optimized for easy copy-pasting, which using props like `className` and `htmlFor` does not encourage. See [the section on prop naming conventions](./special-props-and-tags#prop-naming-conventions) for more information.
### `style`
-The `style` prop value can be an object of CSS declarations. However, unlike React, CSS property names match the case of their CSS equivalents, and we do not add units to numbers. Additionally, Crank allows the style prop to be a CSS string as well.
+The `style` prop can be an object of CSS declarations. However, unlike React, CSS property names match the case of their CSS equivalents, and we do not add units to numbers. Additionally, Crank allows the style prop to be a CSS string as well.
```jsx
Hello
```
+Refer to the guide on [the style prop](./special-props-and-tags#style) for more information.
+
### Event props
-Host elements can be listened to using `onevent` props, but the prop name is always lowercase. Crank also provides an `EventTarget` API for components to add and remove event listeners from the top-level node or nodes of each component. In both cases, Crank does not use a synthetic event system or polyfill events in any way. Refer to [the guide on event handling](./handling-events).
+Host elements can be listened to using `onevent` props, but the prop name will be all lowercase. Crank also provides an `EventTarget` API for components to add and remove event listeners from the top-level node or nodes of each component. In both cases, Crank does not use a synthetic event system or polyfill events in any way. Refer to [the guide on event handling](./handling-events) for a longer explanation of event handling in Crank.
### Controlled and Uncontrolled Props
-Crank does not have a concept of controlled or uncontrolled props, and does not provide `defaultValue`-style props for DOM elements. See [the section on form elements](./handling-events#form-elements) for a detailed description of how Crank handles form elements.
+Crank does not have a concept of controlled or uncontrolled props, and does not provide `defaultValue`-style props for DOM elements. See [the section on form elements](./handling-events#form-elements) for a detailed description of how Crank handles stateful form elements.
### `dangerouslySetInnerHTML`
-Host DOM elements accept an `innerHTML` prop; Crank does not provide the `dangerouslySetInnerHTML={{__html}}` API like React. Alternatively, you can use the special `Raw` tag to insert HTML strings or even DOM nodes directly into an element tree without a parent. [See the guide on the `Raw` element](./special-props-and-tag#raw) for more information.
+Host DOM elements accept an `innerHTML` prop; Crank does not provide the `dangerouslySetInnerHTML={{__html}}` API like React. Alternatively, you can use the special `Raw` tag to insert HTML strings or even DOM nodes directly into an element tree without a parent. Refer to the sections [on the `innerHTML` prop](./special-props-and-tags#innerhtml) and [on the `Raw` tag](./special-props-and-tags#raw) for more information.
## Keys
-Crank provides keyed rendering via the `crank-key` prop. The prop was renamed because “key” is a common word and because the prop is not erased from the props object passed into components.
+Crank provides keyed rendering via the special `crank-key` prop. The prop was renamed because “key” is a common word and because the prop is not erased from the props object passed into components.
Keys work similarly to the way they do in React. The main difference is that Crank does not warn about unkeyed elements which appear in arrays or iterables.
## Refs
-Crank provides the callback-style ref API from React via the `crank-ref` prop. Unlike React, all elements can be read using the `crank-ref` prop, including Fragments, and . See the [guide on the `crank-ref` prop](./special-props-and-tags#crank-ref).
+Crank provides the callback-style ref API from React via the `crank-ref` prop. Unlike React, all elements can be read using the `crank-ref` prop, including Fragment elements. See the [guide on the `crank-ref` prop](./special-props-and-tags#crank-ref).
-You can also access values many other ways. See the [guide on accessing rendered values](./lifecycles#accessing-rendered-values) for more information.
+You can also access rendered values in many other ways. Refer to [this section](./lifecycles#accessing-rendered-values) for more information.
## React Contexts
Because we refer to the `this` keyword of components as “the component’s context” (“controller” would have been three more characters), we refer to the equivalent concept of [React’s Context API](https://reactjs.org/docs/context.html) as “provisions” instead. We use the context methods `provide` and `consume` to define provisions between ancestor and descendant components. See [the guide on provisions](./reusable-logic#provisions) for more information.
From cfa652a66aa72b9289e2ce6978303488c6bfc641 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 28 Jul 2020 12:23:37 -0400
Subject: [PATCH 23/26] add google script
---
website/build.tsx | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/website/build.tsx b/website/build.tsx
index c80c619d3..e4601ed39 100644
--- a/website/build.tsx
+++ b/website/build.tsx
@@ -108,6 +108,19 @@ function Root({title, children, url}: RootProps): Element {
{title}
+
+
From 1ea95d5ff6b274b6dcffa51919f79ab4d5160f5e Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 28 Jul 2020 12:23:59 -0400
Subject: [PATCH 24/26] unhardcode the development watching stuff
---
website/webpack.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/webpack.tsx b/website/webpack.tsx
index b71a386f9..b92d7c547 100644
--- a/website/webpack.tsx
+++ b/website/webpack.tsx
@@ -181,7 +181,7 @@ export class Storage {
.resolve(this.dir, name)
.replace(new RegExp("^" + this.dir + "/"), "");
this.files = {...this.files, [name]: "./" + name};
- const stats = await this.run1();
+ const stats = await this.run();
let assets = stats.assetsByChunkName![name];
if (!Array.isArray(assets)) {
assets = [assets];
From c00ca2716ee1d1607e839375c4b545e88e833b98 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 28 Jul 2020 14:08:57 -0400
Subject: [PATCH 25/26] refresh README
---
README.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 7c717557b..c3c8ce1df 100644
--- a/README.md
+++ b/README.md
@@ -5,19 +5,19 @@ Documentation is available at [crank.js.org](https://crank.js.org). Crank.js is
## Features
### Declarative
-Crank uses the same JSX syntax and diffing algorithm popularized by React, allowing you to write HTML-like syntax directly in your JavaScript.
+Crank uses the same JSX syntax and diffing algorithm popularized by React, allowing you to write HTML-like code directly in JavaScript.
### Just Functions
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.
### Promise-friendly
-Crank provides first-class support for promises. You can use async/await directly in components, and race async components to display fallback UIs.
+Crank provides first-class support for promises. You can define components as async functions and race renderings to display fallback UIs.
### Lightweight
-Crank has no dependencies, and its core is a single file. It currently measures at [4.5KB minified and gzipped](https://bundlephobia.com/result?p=@bikeshaving/crank).
+Crank has no dependencies, and its core is a single file. It currently measures at [4.5KB minified and gzipped]("https://bundlephobia.com/result?p=@bikeshaving/crank").
### Performant
-[According to a standard benchmark](https://krausest.github.io/js-framework-benchmark/current.html), Crank’s performance beats React’s, and is comparable to that of Preact’s and Vue’s.
+[According to benchmarks](https://github.com/krausest/js-framework-benchmark), Crank beats React in terms of speed and memory usage, and is currently comparable to Preact or Vue.
### Extensible
The core renderer can be extended to target alternative environments such as WebGL libraries, terminals, smartphones or smart TVs.
@@ -37,7 +37,7 @@ import {renderer} from "@bikeshaving/crank/dom";
renderer.render(
Hello world
, document.body);
```
-If your environment does not support ESModules (you’ll probably see a message like `SyntaxError: Unexpected token export`), you can import the CommonJS versions of the library under the `cjs` directory.
+If your environment does not support ESModules (you may see a message like `SyntaxError: Unexpected token export`), you can import the CommonJS versions of the library under the `cjs` directory.
```jsx
/** @jsx createElement */
From df4d171a042b17e9f03a30b80267091f006c3f27 Mon Sep 17 00:00:00 2001
From: Brian Kim
Date: Tue, 28 Jul 2020 14:32:07 -0400
Subject: [PATCH 26/26] update CHANGELOG
---
CHANGELOG.md | 73 +++++++++++++++++++++++++++++-----------------------
1 file changed, 41 insertions(+), 32 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f573432a..36c782644 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,66 +1,75 @@
# Changelog
+## [0.3.0] - 2020-07-28
+### Changed
+- The context methods `get` and `set` are now named `provide` and `consume`.
+- Changes to the internal renderer APIs in preparation for initial documentation.
+ - Elements are passed in directly, rather than their tag and props.
+ - The scope is now no longer passed to the patch method.
+### Fixed
+- Fixed errors not propagating from function component children.
+- The context async iterator now suspends at its start if you yield before iterating over it (#137).
## [0.2.1] - 2020-07-02
### Fixed
- Fixed overloads of Context.prototype.set and Context.prototype.get not appearing in the d.ts file.
## [0.2.0] - 2020-07-01
-### Added
-- UMD build
-- crank-ref
-- Context.prototype.schedule
-- Context.prototype.cleanup
-- Context.prototype.props
-- Context.prototype.value
-- Context dispatchEvent capturing
### Changed
-- props are no longer rememebered between renders. For instance, rendering `` and then `` will not cause the checked prop to be deleted on the actual element.
+- Props are no longer rememebered between renders. For instance, rendering `` and then `` will not cause the checked prop to be deleted on the actual element.
- The internal Renderer API has been changed to use inheritance and away from the intrinsic generator API.
-- Renderer.prototype.render and Context.prototype.refresh now return rendered values rather than undefined.
+- `Renderer.prototype.render` and `Context.prototype.refresh` now return rendered values rather than undefined.
- The library is no longer transpiled.
- The esm directory in the package has been deleted. Refer to index, dom and html in the root of the package instead.
- Some types have been simplified
- Tag no longer takes a type parameter.
- - The types Props, FunctionComponent, GeneratorComponent, IntrinsicProps, ChildIterator, ChildGenerator and Key have been deleted.
+ - The types `Props`, `FunctionComponent`, `GeneratorComponent`, `IntrinsicProps`, `ChildIterator`, `ChildGenerator` and `Key` have been removed from the public API.
+### Added
+- UMD build.
+- `crank-ref` props.
+- `Context.prototype.schedule`.
+- `Context.prototype.cleanup`.
+- `Context.prototype.props`.
+- `Context.prototype.value`.
+- `dispatchEvent` now does bubbling and capturing correctly.
### Fixed
- Performance improvements in terms of execution time, runtime memory costs, and library size.
-- Fixed Context.prototype.dispatchEvent not using stopPropagation and stopImmediatePropagation.
+- Fixed `Context.prototype.dispatchEvent` not responding to `stopPropagation` and `stopImmediatePropagation`.
- Fixed nested SVG elements not rendering SVGElements in some cases.
-- Improved error throwing to work with the call stack.
+- Improved error handling.
## [0.1.6] - 2020-05-25
-- Backed out of a performance optimization where async generator components caused siblings to remain in the DOM
+- Backed out of a performance optimization where async generator components caused siblings to remain in the DOM.
## [0.1.5] - 2020-05-21
-- Fixed SVG attributes causing readonly errors #119
+- Fixed SVG attributes causing readonly errors (#119).
## [0.1.4] - 2020-05-17
### Fixed
-- Added support for SVG elements in the DOM renderer #110
+- Added support for SVG elements in the DOM renderer (#110).
- **basic performance improvements, you want to upgrade to at least 0.1.4 and probably 0.2 when it is released**
### Changed
-- got rid of the HostContext class in favor of exposing the HostNode directly to intrinsics
-- added a way to pass information in a preorder traversal to child host nodes (search for Scoper/Scopes in the codebase)
-- added new flags for intrinsics to optimize performance (search for dirty in the codebase)
+- Got rid of the `HostContext` class in favor of exposing the HostNode directly to intrinsics.
+- Added a way to pass information in a preorder traversal to child host nodes (search for Scoper/Scopes in the codebase).
+- Added new flags for intrinsics to optimize performance (search for dirty in the codebase).
## [0.1.3] - 2020-05-05
### Fixed
-- Made event-target-shim a direct dependency so TypeScript doesn’t error #95
+- Made `event-target-shim` a direct dependency so TypeScript doesn’t error (#95).
### Changed
-- Updated the types of Component and Context to take explicit prop types #51
-- Allow createElement to be passed anything as Children #97
-- Allow arbitrary elements to be passed to renderer.render #97
+- Updated the types of `Component` and `Context` to take explicit prop types (#51).
+- Allow createElement to be passed anything as `Children` (#97).
+- Allow arbitrary elements to be passed to `Renderer.prototype.render` (#97).
## [0.1.2] - 2020-04-29
### Fixed
-- Fixed Copy element tag not using Symbol.for #69
-- Fixed event listeners not being properly removed when component is unmounted #70
-- Prevented child components from causing parent components to rerender while it is already rerendering #70
-- Fixed keyed element logic when an unkeyed element is placed before multiple keyed elements previously rendered
-- Fixed a deadlock when errors are thrown back into async generator components: #77
+- Fixed `Copy` element tag not using `Symbol.for` (#69).
+- Fixed event listeners not being properly removed when component is unmounted (#70).
+- Prevented child components from causing parent components to rerender while it is already rerendering (#70).
+- Fixed keyed element logic when an unkeyed element is placed before multiple keyed elements previously rendered.
+- Fixed a deadlock when errors are thrown back into async generator components (#77).
## [0.1.1] - 2020-04-25
### Fixed
-- Corrected boolean props not working correctly in html renderer #44
-- Guarded against potential xss in style objects #44
-- Wrapped non-string iterables in an implicit Fragment element #63
-- Made sure stateless renders are unmounted #63
+- Corrected boolean props not working correctly in html renderer (#44).
+- Guarded against potential xss in style objects (#44).
+- Wrapped non-string iterables in an implicit Fragment element (#63).
+- Made sure stateless renders are unmounted (#63).
## [0.1.0] - 2020-04-14
### Added