From 089b99ed5acc4a0007ee34309c6d5075c5789612 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 5 Feb 2024 01:18:58 -0600 Subject: [PATCH 1/6] chore: Backport much of #793 Co-authored-by: Marvin Hagemeister --- content/en/tutorial/01-vdom.md | 2 +- content/en/tutorial/02-events.md | 2 +- content/en/tutorial/03-components.md | 2 +- content/en/tutorial/04-state.md | 2 +- content/en/tutorial/05-refs.md | 2 +- content/en/tutorial/07-side-effects.md | 2 +- content/en/tutorial/08-keys.md | 2 +- content/en/tutorial/09-error-handling.md | 2 +- content/kr/tutorial/01-vdom.md | 2 +- content/kr/tutorial/02-events.md | 2 +- content/ru/tutorial/01-vdom.md | 2 +- content/ru/tutorial/02-events.md | 2 +- content/ru/tutorial/03-components.md | 2 +- content/ru/tutorial/04-state.md | 2 +- content/ru/tutorial/05-refs.md | 2 +- content/ru/tutorial/07-side-effects.md | 2 +- content/ru/tutorial/08-keys.md | 2 +- content/ru/tutorial/09-error-handling.md | 2 +- content/zh/tutorial/01-vdom.md | 2 +- content/zh/tutorial/02-events.md | 2 +- jsconfig.json | 4 +- package-lock.json | 494 ++++++++--- package.json | 7 +- patches/preact-cli+4.0.0-next.6.patch | 57 ++ src/components/app.js | 64 +- src/components/blog-meta/index.js | 62 ++ src/components/blog-meta/style.module.css | 12 + src/components/blog-overview/index.js | 5 +- src/components/branding/index.js | 12 +- src/components/code-block/index.js | 129 +-- src/components/code-block/prism.worker.js | 3 +- src/components/content-region/index.js | 17 +- src/components/controllers/blog-page.js | 40 + src/components/controllers/doc-page.js | 85 ++ src/components/controllers/index.js | 11 - src/components/controllers/markdown-region.js | 30 + src/components/controllers/not-found.js | 25 + src/components/controllers/page.js | 14 + src/components/controllers/page/index.js | 175 ---- src/components/controllers/page/sidebar.js | 68 -- .../controllers/{page => }/style.module.css | 14 +- src/components/controllers/tutorial/index.js | 124 +-- src/components/controllers/utils.js | 87 +- src/components/doc-version/index.js | 28 +- src/components/edit-button/index.js | 48 +- src/components/footer/index.js | 6 +- src/components/header/index.js | 14 +- src/components/logo.js | 6 +- src/components/routes.js | 141 ++- src/components/sidebar/index.js | 60 ++ .../page => sidebar}/sidebar-nav.js | 12 +- .../page => sidebar}/sidebar-nav.module.css | 0 .../style.module.css} | 0 src/components/table-of-contents/index.js | 28 +- src/config.json | 800 +++++++++--------- src/lib/content.js | 25 +- src/lib/docs.js | 4 +- src/lib/i18n.js | 70 +- src/lib/route-utils.js | 38 + src/lib/toggle-overlay.js | 2 +- src/lib/use-resource.js | 67 ++ src/prerender.js | 29 +- src/pwa.js | 2 + src/types.d.ts | 1 + 64 files changed, 1671 insertions(+), 1289 deletions(-) create mode 100644 patches/preact-cli+4.0.0-next.6.patch create mode 100644 src/components/blog-meta/index.js create mode 100644 src/components/blog-meta/style.module.css create mode 100644 src/components/controllers/blog-page.js create mode 100644 src/components/controllers/doc-page.js delete mode 100644 src/components/controllers/index.js create mode 100644 src/components/controllers/markdown-region.js create mode 100644 src/components/controllers/not-found.js create mode 100644 src/components/controllers/page.js delete mode 100644 src/components/controllers/page/index.js delete mode 100644 src/components/controllers/page/sidebar.js rename src/components/controllers/{page => }/style.module.css (87%) create mode 100644 src/components/sidebar/index.js rename src/components/{controllers/page => sidebar}/sidebar-nav.js (91%) rename src/components/{controllers/page => sidebar}/sidebar-nav.module.css (100%) rename src/components/{controllers/page/sidebar.module.css => sidebar/style.module.css} (100%) create mode 100644 src/lib/route-utils.js create mode 100644 src/lib/use-resource.js diff --git a/content/en/tutorial/01-vdom.md b/content/en/tutorial/01-vdom.md index e278e086f..16232424d 100644 --- a/content/en/tutorial/01-vdom.md +++ b/content/en/tutorial/01-vdom.md @@ -196,7 +196,7 @@ useResult(function(result) { var p = result.output.querySelector('p'); var hasColor = p && p.style && p.style.color === 'purple'; if (hasEm && hasColor) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, []); ``` diff --git a/content/en/tutorial/02-events.md b/content/en/tutorial/02-events.md index f92f5374e..136a138eb 100644 --- a/content/en/tutorial/02-events.md +++ b/content/en/tutorial/02-events.md @@ -61,7 +61,7 @@ useRealm(function (realm) { var win = realm.globalThis; var prevConsoleLog = win.console.log; win.console.log = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/en/tutorial/03-components.md b/content/en/tutorial/03-components.md index b85b086b6..297dc4027 100644 --- a/content/en/tutorial/03-components.md +++ b/content/en/tutorial/03-components.md @@ -254,7 +254,7 @@ useRealm(function (realm) { win.console.log = function() { if (hasComponent && check) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/en/tutorial/04-state.md b/content/en/tutorial/04-state.md index ce0e2a0d3..b65e111ba 100644 --- a/content/en/tutorial/04-state.md +++ b/content/en/tutorial/04-state.md @@ -161,7 +161,7 @@ useResult(function () { } if (Number(text2[1]) === Number(text[1]) + 1) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, 10); } diff --git a/content/en/tutorial/05-refs.md b/content/en/tutorial/05-refs.md index a696d7e84..e720773a4 100644 --- a/content/en/tutorial/05-refs.md +++ b/content/en/tutorial/05-refs.md @@ -133,7 +133,7 @@ function patch(input) { input.__patched = true; var old = input.focus; input.focus = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return old.call(this); }; } diff --git a/content/en/tutorial/07-side-effects.md b/content/en/tutorial/07-side-effects.md index efe1ca2b3..12f91a9c8 100644 --- a/content/en/tutorial/07-side-effects.md +++ b/content/en/tutorial/07-side-effects.md @@ -138,7 +138,7 @@ useRealm(function (realm) { var prevConsoleLog = win.console.log; win.console.log = function(m, s) { if (/Count is now/.test(m) && s === 1) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/en/tutorial/08-keys.md b/content/en/tutorial/08-keys.md index 9e6259a86..4a0c3fd3c 100644 --- a/content/en/tutorial/08-keys.md +++ b/content/en/tutorial/08-keys.md @@ -307,7 +307,7 @@ useRealm(function (realm) { /learn preact/i.test(c[0].textContent) && /make an awesome app/i.test(c[1].textContent) ) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } } diff --git a/content/en/tutorial/09-error-handling.md b/content/en/tutorial/09-error-handling.md index 99ccc1680..86e42f582 100644 --- a/content/en/tutorial/09-error-handling.md +++ b/content/en/tutorial/09-error-handling.md @@ -98,7 +98,7 @@ useResult(function(result) { oe.apply(this, arguments); setTimeout(function() { if (result.output.textContent.match(/error/i)) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, 10); }; diff --git a/content/kr/tutorial/01-vdom.md b/content/kr/tutorial/01-vdom.md index 44cadba4c..2e200bf9f 100644 --- a/content/kr/tutorial/01-vdom.md +++ b/content/kr/tutorial/01-vdom.md @@ -145,7 +145,7 @@ useResult(function(result) { var p = result.output.querySelector('p'); var hasColor = p && p.style && p.style.color === 'purple'; if (hasEm && hasColor) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, []); ``` diff --git a/content/kr/tutorial/02-events.md b/content/kr/tutorial/02-events.md index 8314f6395..cea550d09 100644 --- a/content/kr/tutorial/02-events.md +++ b/content/kr/tutorial/02-events.md @@ -54,7 +54,7 @@ useRealm(function (realm) { var win = realm.globalThis; var prevConsoleLog = win.console.log; win.console.log = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/ru/tutorial/01-vdom.md b/content/ru/tutorial/01-vdom.md index 69a7e8617..86b7270bf 100644 --- a/content/ru/tutorial/01-vdom.md +++ b/content/ru/tutorial/01-vdom.md @@ -152,7 +152,7 @@ useResult(function(result) { var p = result.output.querySelector('p'); var hasColor = p && p.style && p.style.color === 'purple'; if (hasEm && hasColor) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, []); ``` diff --git a/content/ru/tutorial/02-events.md b/content/ru/tutorial/02-events.md index 247e05f6d..eeb8460b3 100644 --- a/content/ru/tutorial/02-events.md +++ b/content/ru/tutorial/02-events.md @@ -48,7 +48,7 @@ useRealm(function (realm) { var win = realm.globalThis; var prevConsoleLog = win.console.log; win.console.log = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/ru/tutorial/03-components.md b/content/ru/tutorial/03-components.md index b2e2137b8..06dbff203 100644 --- a/content/ru/tutorial/03-components.md +++ b/content/ru/tutorial/03-components.md @@ -188,7 +188,7 @@ useRealm(function (realm) { win.console.log = function() { if (hasComponent && check) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/ru/tutorial/04-state.md b/content/ru/tutorial/04-state.md index 859c3b857..f9ed7ab94 100644 --- a/content/ru/tutorial/04-state.md +++ b/content/ru/tutorial/04-state.md @@ -113,7 +113,7 @@ useResult(function () { } if (Number(text2[1]) === Number(text[1]) + 1) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, 10); } diff --git a/content/ru/tutorial/05-refs.md b/content/ru/tutorial/05-refs.md index aa5118eba..f11749c83 100644 --- a/content/ru/tutorial/05-refs.md +++ b/content/ru/tutorial/05-refs.md @@ -103,7 +103,7 @@ function patch(input) { input.__patched = true; var old = input.focus; input.focus = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return old.call(this); }; } diff --git a/content/ru/tutorial/07-side-effects.md b/content/ru/tutorial/07-side-effects.md index 600232439..698c499c3 100644 --- a/content/ru/tutorial/07-side-effects.md +++ b/content/ru/tutorial/07-side-effects.md @@ -113,7 +113,7 @@ useRealm(function (realm) { var prevConsoleLog = win.console.log; win.console.log = function(m, s) { if (/Счётчик: /.test(m) && s === 1) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } return prevConsoleLog.apply(win.console, arguments); }; diff --git a/content/ru/tutorial/08-keys.md b/content/ru/tutorial/08-keys.md index 869f2f06b..9b2761357 100644 --- a/content/ru/tutorial/08-keys.md +++ b/content/ru/tutorial/08-keys.md @@ -240,7 +240,7 @@ useRealm(function (realm) { /изучить preact/i.test(c[0].textContent) && /сделать крутое приложение/i.test(c[1].textContent) ) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } } diff --git a/content/ru/tutorial/09-error-handling.md b/content/ru/tutorial/09-error-handling.md index 532a81b38..3d4975576 100644 --- a/content/ru/tutorial/09-error-handling.md +++ b/content/ru/tutorial/09-error-handling.md @@ -73,7 +73,7 @@ useResult(function(result) { oe.apply(this, arguments); setTimeout(function() { if (result.output.textContent.match(/ошибка/i)) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, 10); }; diff --git a/content/zh/tutorial/01-vdom.md b/content/zh/tutorial/01-vdom.md index 041f224cd..e63f65c3e 100644 --- a/content/zh/tutorial/01-vdom.md +++ b/content/zh/tutorial/01-vdom.md @@ -161,7 +161,7 @@ useResult(function(result) { var p = result.output.querySelector('p'); var hasColor = p && p.style && p.style.color === 'purple'; if (hasEm && hasColor) { - store.setState({ solved: true }); + solutionCtx.setSolved(true); } }, []); ``` diff --git a/content/zh/tutorial/02-events.md b/content/zh/tutorial/02-events.md index db46d89ff..774f2d9e2 100644 --- a/content/zh/tutorial/02-events.md +++ b/content/zh/tutorial/02-events.md @@ -48,7 +48,7 @@ useRealm(function (realm) { var win = realm.globalThis; var prevConsoleLog = win.console.log; win.console.log = function() { - store.setState({ solved: true }); + solutionCtx.setSolved(true); return prevConsoleLog.apply(win.console, arguments); }; diff --git a/jsconfig.json b/jsconfig.json index caaa651a3..e401488e3 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "ESNext", - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "ESNext", + "moduleResolution": "Node", "resolveJsonModule": true, "jsx": "react-jsx", "jsxImportSource": "preact", diff --git a/package-lock.json b/package-lock.json index a672460fa..c208ae948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "hasInstallScript": true, "license": "MIT", "dependencies": { "@preact/signals": "^1.1.3", @@ -20,12 +21,11 @@ "node-fetch": "^2.6.1", "preact": "10.15.1", "preact-custom-element": "^4.3.0", + "preact-iso": "2.2.0", "preact-markup": "^2.1.1", "preact-render-to-string": "^5.2.6", - "preact-router": "^3.1.0", "rollup": "^2.79.1", "sucrase": "^3.32.0", - "unistore": "^3.5.1", "yaml": "^1.7.2" }, "devDependencies": { @@ -38,6 +38,7 @@ "jsdom": "^15.2.1", "lint-staged": "^15.2.0", "netlify-lambda": "^2.0.16", + "patch-package": "^8.0.0", "postcss-custom-properties": "^13.3.2", "postcss-import": "^14.0.0", "postcss-nesting": "^12.0.1", @@ -3305,6 +3306,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "node_modules/abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -8270,6 +8277,15 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -8504,18 +8520,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -8685,6 +8689,21 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -10781,12 +10800,36 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json-stable-stringify/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -10805,6 +10848,27 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -10857,6 +10921,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -11752,10 +11825,13 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { "version": "7.0.4", @@ -13642,6 +13718,124 @@ "node": ">=0.10.0" } }, + "node_modules/patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/patch-package/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -14694,6 +14888,15 @@ "preact": "10.x" } }, + "node_modules/preact-iso": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/preact-iso/-/preact-iso-2.2.0.tgz", + "integrity": "sha512-BIUOYJWnfmOcgFiuAqGTLkqvC2Rqlpn3aLP/ik8kT8afKdLrG3CvBEpSQivMrFY1o2InGX/jEL44ofmoQdoieA==", + "peerDependencies": { + "preact": ">=10", + "preact-render-to-string": ">=5" + } + }, "node_modules/preact-markup": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/preact-markup/-/preact-markup-2.1.1.tgz", @@ -14713,14 +14916,6 @@ "preact": ">=10" } }, - "node_modules/preact-router": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/preact-router/-/preact-router-3.2.1.tgz", - "integrity": "sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==", - "peerDependencies": { - "preact": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -18225,19 +18420,6 @@ "node": ">=8" } }, - "node_modules/unistore": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/unistore/-/unistore-3.5.2.tgz", - "integrity": "sha512-2Aa4eX0Ua1umyiI3Eai6Li+wXYOHgaDBGOPB3Hvw7PAVuD30TAyh5kS4yNKb2fLDbQgizvPhKQRcYnOdfsm4VQ==", - "peerDependenciesMeta": { - "preact": { - "optional": true - }, - "react": { - "optional": true - } - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -19685,21 +19867,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/workbox-build/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/workbox-build/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -19735,18 +19902,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/workbox-build/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/workbox-build/node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -22674,6 +22829,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -26488,6 +26649,15 @@ "pkg-dir": "^4.1.0" } }, + "find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "requires": { + "micromatch": "^4.0.2" + } + }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -26661,16 +26831,6 @@ "resolve-from": "^4.0.0" } }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -26800,6 +26960,18 @@ } } }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -28363,6 +28535,26 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -28384,6 +28576,22 @@ "minimist": "^1.2.0" } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true + }, "jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -28424,6 +28632,15 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, "kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -29113,9 +29330,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "minipass": { @@ -30633,6 +30850,86 @@ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true }, + "patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "dependencies": { + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true + } + } + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -31308,6 +31605,12 @@ "integrity": "sha512-5hG7nQhU4e7RNfCEQklaUqYQiiyibLuJ2wbhR+E2v1m8m9NDsJok5MykW/Nx0YLLBcXr8xnkap6DwByGy2TzDA==", "requires": {} }, + "preact-iso": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/preact-iso/-/preact-iso-2.2.0.tgz", + "integrity": "sha512-BIUOYJWnfmOcgFiuAqGTLkqvC2Rqlpn3aLP/ik8kT8afKdLrG3CvBEpSQivMrFY1o2InGX/jEL44ofmoQdoieA==", + "requires": {} + }, "preact-markup": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/preact-markup/-/preact-markup-2.1.1.tgz", @@ -31322,12 +31625,6 @@ "pretty-format": "^3.8.0" } }, - "preact-router": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/preact-router/-/preact-router-3.2.1.tgz", - "integrity": "sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==", - "requires": {} - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -34096,11 +34393,6 @@ "crypto-random-string": "^2.0.0" } }, - "unistore": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/unistore/-/unistore-3.5.2.tgz", - "integrity": "sha512-2Aa4eX0Ua1umyiI3Eai6Li+wXYOHgaDBGOPB3Hvw7PAVuD30TAyh5kS4yNKb2fLDbQgizvPhKQRcYnOdfsm4VQ==" - }, "universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -35237,18 +35529,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -35278,16 +35558,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", diff --git a/package.json b/package.json index 4c754f882..0aacadebf 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "prebuild": "rimraf build/assets && rimraf build/content", "lint": "eslint src test", "format": "prettier --write \"{src,test}/**/*.{css,js,json}\"", - "prepare": "husky install" + "prepare": "husky install", + "postinstall": "patch-package" }, "eslintConfig": { "extends": "developit", @@ -64,6 +65,7 @@ "jsdom": "^15.2.1", "lint-staged": "^15.2.0", "netlify-lambda": "^2.0.16", + "patch-package": "^8.0.0", "postcss-custom-properties": "^13.3.2", "postcss-import": "^14.0.0", "postcss-nesting": "^12.0.1", @@ -88,12 +90,11 @@ "node-fetch": "^2.6.1", "preact": "10.15.1", "preact-custom-element": "^4.3.0", + "preact-iso": "2.2.0", "preact-markup": "^2.1.1", "preact-render-to-string": "^5.2.6", - "preact-router": "^3.2.1", "rollup": "^2.79.1", "sucrase": "^3.32.0", - "unistore": "^3.5.1", "yaml": "^1.7.2" } } diff --git a/patches/preact-cli+4.0.0-next.6.patch b/patches/preact-cli+4.0.0-next.6.patch new file mode 100644 index 000000000..2836e08b2 --- /dev/null +++ b/patches/preact-cli+4.0.0-next.6.patch @@ -0,0 +1,57 @@ +diff --git a/node_modules/preact-cli/src/lib/webpack/prerender.js b/node_modules/preact-cli/src/lib/webpack/prerender.js +index 945a4e0..e8323d3 100644 +--- a/node_modules/preact-cli/src/lib/webpack/prerender.js ++++ b/node_modules/preact-cli/src/lib/webpack/prerender.js +@@ -2,7 +2,7 @@ const { red, yellow } = require('kleur'); + const { resolve } = require('path'); + const { readFileSync } = require('fs'); + const stackTrace = require('stack-trace'); +-const URL = require('url'); ++const { URL } = require('url'); + const { SourceMapConsumer } = require('source-map'); + + module.exports = async function (config, params) { +@@ -11,14 +11,14 @@ module.exports = async function (config, params) { + let entry = resolve(config.dest, './ssr-build/ssr-bundle.js'); + let url = params.url || '/'; + +- global.history = {}; +- global.location = { ...URL.parse(url) }; ++ global.history = /** @type {object} */ ({}); ++ global.location = /** @type {object} */ (new URL(url, 'http://localhost')); + + try { + let m = require(entry), +- app = (m && m.default) || m; ++ vnode = (m && m.default) || m; + +- if (typeof app !== 'function') { ++ if (typeof vnode !== 'function') { + // eslint-disable-next-line no-console + console.warn( + 'Entry does not export a Component function/class, aborting prerendering.' +@@ -29,7 +29,23 @@ module.exports = async function (config, params) { + const renderToString = require(require.resolve('preact-render-to-string', { + paths: [config.cwd], + })); +- return renderToString(preact.h(app, { ...params, url })); ++ ++ vnode = preact.h(vnode, { ...params, url }); ++ ++ // Slightly modified version of preact-iso's `prerender()` ++ let tries; ++ const maxDepth = 10; ++ const render = () => { ++ if (++tries > maxDepth) return; ++ try { ++ return renderToString(vnode); ++ } catch (e) { ++ if (e && e.then) return e.then(render); ++ throw e; ++ } ++ }; ++ ++ return await render(); + } catch (err) { + let stack = stackTrace.parse(err).filter(s => s.getFileName() === entry)[0]; + if (!stack) { diff --git a/src/components/app.js b/src/components/app.js index caf615d1d..fa588e000 100644 --- a/src/components/app.js +++ b/src/components/app.js @@ -1,46 +1,24 @@ -import { Component } from 'preact'; -import { Provider } from 'unistore/preact'; -import createStore from '../store'; -import Routes from './routes'; +import { LocationProvider, ErrorBoundary } from 'preact-iso'; +import { LanguageProvider } from '../lib/i18n'; +// TODO: SolutionProvider should really just wrap the tutorial, +// but that requires a bit of refactoring +import { SolutionProvider } from './controllers/tutorial/index.js'; import Header from './header'; -import { storeCtx } from './store-adapter'; -import { getCurrentDocVersion } from '../lib/docs'; - -export default class App extends Component { - store = createStore({ - url: this.props.url || location.pathname + location.search, - lang: 'en', - preactVersion: this.props.preactVersion || '10.11.3', - docVersion: getCurrentDocVersion(location.pathname), - toc: null - }); - - handleUrlChange = ({ url }) => { - let prev = this.store.getState().url || '/'; - if (url !== prev) { - this.store.setState({ - ...this.store.getState(), - toc: null, - url, - docVersion: getCurrentDocVersion(url) - }); - if (typeof ga === 'function') { - ga('send', 'pageview', url); - } - } - }; +import Routes from './routes'; - render() { - const { url } = this.store.getState(); - return ( - - -
-
- -
-
-
- ); - } +export default function App({ preactVersion }) { + return ( + + + + +
+
+ +
+
+
+
+
+ ); } diff --git a/src/components/blog-meta/index.js b/src/components/blog-meta/index.js new file mode 100644 index 000000000..70100d986 --- /dev/null +++ b/src/components/blog-meta/index.js @@ -0,0 +1,62 @@ +import { Time } from '../time'; +import config from '../../config.json'; +import style from './style.module.css'; + +export default function BlogMeta({ meta }) { + return ( +
+ {meta.date &&
+ ); +} + +function AuthorLinks({ authorData, author, i, arr }) { + return ( + + {authorData ? ( + + {author} + + ) : ( + {author} + )} + {i < arr.length - 2 ? ', ' : i === arr.length - 2 ? ' and ' : null} + + ); +} diff --git a/src/components/blog-meta/style.module.css b/src/components/blog-meta/style.module.css new file mode 100644 index 000000000..cd7ccb07e --- /dev/null +++ b/src/components/blog-meta/style.module.css @@ -0,0 +1,12 @@ +.blogMeta { + /* Negative bottom margin negates content-region's top margin */ + margin: 2.5rem auto -3rem; + z-index: 10; + padding: 0.5em 1rem 0.25rem; + width: 100%; + max-width: var(--content-width); + + .authors { + display: inline-block; + } +} diff --git a/src/components/blog-overview/index.js b/src/components/blog-overview/index.js index 435a1be26..f09723d90 100644 --- a/src/components/blog-overview/index.js +++ b/src/components/blog-overview/index.js @@ -1,12 +1,11 @@ import config from '../../config.json'; -import { useTranslation } from '../../lib/i18n'; +import { useLanguage, useTranslation } from '../../lib/i18n'; import { getRouteName } from '../header'; -import { useStore } from '../store-adapter'; import { Time } from '../time'; import s from './style.module.css'; export default function BlogOverview() { - const { lang } = useStore(['lang']).state; + const [lang] = useLanguage(); const continueReading = useTranslation('continueReading'); return ( diff --git a/src/components/branding/index.js b/src/components/branding/index.js index c10c9878b..d292ca407 100644 --- a/src/components/branding/index.js +++ b/src/components/branding/index.js @@ -21,10 +21,18 @@ function LogoVariation({ name, alt }) { height="64" /> diff --git a/src/components/code-block/index.js b/src/components/code-block/index.js index 303c6f32c..26e85b9da 100644 --- a/src/components/code-block/index.js +++ b/src/components/code-block/index.js @@ -1,72 +1,29 @@ -import { useState, useMemo, useRef, useEffect } from 'preact/hooks'; -import { Link } from 'preact-router'; +import { useMemo } from 'preact/hooks'; import * as Comlink from 'comlink'; -import cx from '../../lib/cx'; - -let highlight; -(async function initHighlight() { - ({ highlight } = PRERENDER - ? import('./prism.worker.js') - : Comlink.wrap( - new Worker( - /* webpackChunkName: "prism-worker" */ new URL( - './prism.worker.js', - import.meta.url - ) +import { FakeSuspense, useResource } from '../../lib/use-resource'; + +const { highlight } = PRERENDER + ? require('./prism.worker.js') + : Comlink.wrap( + new Worker( + /* webpackChunkName: "prism-worker" */ new URL( + './prism.worker.js', + import.meta.url ) - )); -})(); - -function useFuture(initializer, params) { - const getInitialState = () => { - try { - const value = initializer(); - if (value && value.then) { - if ('_value' in value) return [value._value]; - if ('_error' in value) return [undefined, value._error]; - return [undefined, undefined, value]; - } - return [value]; - } catch (err) { - return [undefined, err]; - } - }; - - const [pair, setValue] = useState(getInitialState); - - // only run on changes, not initial mount - const isFirstRun = useRef(true); - useEffect(() => { - if (isFirstRun.current) return (isFirstRun.current = false); - setValue(getInitialState()); - }, params || []); - - const pending = pair[2]; - if (pending) { - if (!pending._processing) { - pending._processing = true; - pending - .then(value => { - pending._value = value; - setValue([value]); - }) - .catch(err => { - pending._error = err; - setValue([undefined, err]); - }); - } - } - return pair; -} + ) + ); + +/** + * @param {{ code: string, lang: string }} props + */ +function HighlightedCode({ code, lang }) { + const highlighted = useResource(() => highlight(code, lang), [code, lang]); -const CACHE = {}; -function cachedHighlight(code, lang) { - const id = lang + '\n' + code; - return CACHE[id] || (CACHE[id] = highlight(code, lang)); + const htmlObj = useMemo(() => ({ __html: highlighted }), [highlighted]); + return ; } -function HighlightedCodeBlock({ code, lang, ...props }) { - let repl = false; +function processRepl(code, repl) { let source = code; if (code.startsWith('// --repl')) { repl = true; @@ -102,29 +59,32 @@ function HighlightedCodeBlock({ code, lang, ...props }) { } } - const [highlighted, error, pending] = useFuture( - () => cachedHighlight(code, lang), - [code, lang] - ); + return [code, source, repl]; +} + +/** + * @param {{ code: string, lang: string, repl?: string }} props + */ +function HighlightedCodeBlock({ code, lang }) { + let repl = false, + source = code; + + [code, source, repl] = processRepl(source, repl); - const canHighlight = !!pending || !error; - const html = - (canHighlight && highlighted) || - code.replace(//g, '>'); - const htmlObj = useMemo(() => ({ __html: html }), [html]); + // Show unhighlighted code as a fallback until we're ready + const fallback = {code}; return ( -
+
-				
+				
+					
+				
 			
{repl && ( - + Run in REPL - + )}
); @@ -143,14 +103,7 @@ const CodeBlock = props => { )[1]; const firstChild = getChild(child.props); const code = String(firstChild || '').replace(/(^\s+|\s+$)/g, ''); - return ( - - ); + return ; } return
;
diff --git a/src/components/code-block/prism.worker.js b/src/components/code-block/prism.worker.js
index 05f1342d0..0354a1c92 100644
--- a/src/components/code-block/prism.worker.js
+++ b/src/components/code-block/prism.worker.js
@@ -7,7 +7,8 @@ export function highlight(code, lang) {
 	if (prism.languages[lang] != null) {
 		return prism.highlight(code, prism.languages[lang], lang);
 	}
-	throw Error(`Unknown language: ${lang}`);
+	//console.error(`Unknown language: ${lang}`);
+	return code;
 }
 
 // .expose will throw in SSR env
diff --git a/src/components/content-region/index.js b/src/components/content-region/index.js
index 995d001b2..d9fdde6be 100644
--- a/src/components/content-region/index.js
+++ b/src/components/content-region/index.js
@@ -2,6 +2,7 @@ import Markup from 'preact-markup';
 import widgets from '../widgets';
 import style from './style.module.css';
 import { useTranslation } from '../../lib/i18n';
+import { TocContext } from '../table-of-contents';
 
 const COMPONENTS = {
 	...widgets,
@@ -60,13 +61,15 @@ export default function ContentRegion({ content, components, ...props }) {
 	return (
 		
 			{content && (
-				
+				
+					
+				
 			)}
 			{hasNav && (
 				
diff --git a/src/components/controllers/blog-page.js b/src/components/controllers/blog-page.js new file mode 100644 index 000000000..dc23d7d8b --- /dev/null +++ b/src/components/controllers/blog-page.js @@ -0,0 +1,40 @@ +import { useRoute, useLocation } from 'preact-iso'; +import { useContent } from '../../lib/use-resource'; +import { useTitle, useDescription } from './utils'; +import { NotFound } from './not-found'; +import { useLanguage } from '../../lib/i18n'; +import { MarkdownRegion } from './markdown-region'; +import Footer from '../footer/index'; +import { blogRoutes } from '../../lib/route-utils'; +import style from './style.module.css'; + +export default function BlogPage() { + const { params } = useRoute(); + const { slug } = params; + + if (!blogRoutes[`/blog/${slug}`]) { + return ; + } + + return ; +} + +function BlogLayout() { + const { url } = useLocation(); + const [lang] = useLanguage(); + + const { html, meta } = useContent([lang, url === '/' ? 'index' : url]); + useTitle(meta.title); + useDescription(meta.description); + + return ( +
+
+
+ +
+
+
+
+ ); +} diff --git a/src/components/controllers/doc-page.js b/src/components/controllers/doc-page.js new file mode 100644 index 000000000..e24f89e49 --- /dev/null +++ b/src/components/controllers/doc-page.js @@ -0,0 +1,85 @@ +import { useRoute, useLocation } from 'preact-iso'; +import { useContent } from '../../lib/use-resource'; +import { useTitle, useDescription } from './utils'; +import config from '../../config.json'; +import { NotFound } from './not-found'; +import cx from '../../lib/cx'; +import { LATEST_MAJOR, isDocPage } from '../../lib/docs'; +import { useLanguage } from '../../lib/i18n'; +import { MarkdownRegion } from './markdown-region'; +import Sidebar from '../sidebar'; +import Footer from '../footer/index'; +import { docRoutes } from '../../lib/route-utils'; +import style from './style.module.css'; + +export function DocPage() { + const { params } = useRoute(); + const { version, name } = params; + + if (!docRoutes[version]['/' + name]) { + return ; + } + + return ; +} + +export function DocLayout() { + const { url } = useLocation(); + const [lang] = useLanguage(); + + const { html, meta } = useContent([lang, url === '/' ? 'index' : url]); + useTitle(meta.title); + useDescription(meta.description); + + const hasSidebar = meta.toc !== false && isDocPage(url); + + return ( +
+
+ {hasSidebar && ( +
+ +
+ )} +
+ + +
+
+
+
+ ); +} + +function OldDocsWarning() { + const { name, version } = useRoute().params; + const { url } = useLocation(); + + if (!isDocPage(url) || version === LATEST_MAJOR) { + return null; + } + + const latestExists = config.docs[LATEST_MAJOR].some(section => + section.routes.some(route => route.path === '/' + name) + ); + + return ( +
+ You are viewing the documentation for an older version of Preact. + {latestExists ? ( + <> + {' '} + Switch to the{' '} + current version. + + ) : ( + <> + {' '} + Get started with the{' '} + current version + . + + )} +
+ ); +} diff --git a/src/components/controllers/index.js b/src/components/controllers/index.js deleted file mode 100644 index b80fe0cc2..000000000 --- a/src/components/controllers/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Page from './page'; -import Repl from 'async!./repl'; -import Tutorial from 'async!./tutorial'; - -export default { - default: Page, - error: Page, - Repl, - Blog: Page, - Tutorial -}; diff --git a/src/components/controllers/markdown-region.js b/src/components/controllers/markdown-region.js new file mode 100644 index 000000000..2c0328214 --- /dev/null +++ b/src/components/controllers/markdown-region.js @@ -0,0 +1,30 @@ +import { useLocation } from 'preact-iso'; +import EditThisPage from '../edit-button'; +import ContentRegion from '../content-region'; +import BlogMeta from '../blog-meta'; + +/** + * @param {object} props + * @propery {string} html + * @propery {any} meta + * @propery {any} [components] + */ +export function MarkdownRegion({ html, meta, components }) { + const { url } = useLocation(); + + const canEdit = url !== '/' && !url.startsWith('/tutorial'); + const isBlogArticle = url.startsWith('/blog/'); + + return ( + <> + {canEdit && } + {isBlogArticle && } + + + ); +} diff --git a/src/components/controllers/not-found.js b/src/components/controllers/not-found.js new file mode 100644 index 000000000..6a64acbc9 --- /dev/null +++ b/src/components/controllers/not-found.js @@ -0,0 +1,25 @@ +import { useLanguage } from '../../lib/i18n'; +import { useContent } from '../../lib/use-resource'; +import { useTitle, useDescription } from './utils'; +import Footer from '../footer'; +import { MarkdownRegion } from './markdown-region'; +import style from './style.module.css'; + +export function NotFound() { + const [lang] = useLanguage(); + + const { html, meta } = useContent([lang, '404']); + useTitle(meta.title); + useDescription(meta.description); + + return ( +
+
+
+ +
+
+
+
+ ); +} diff --git a/src/components/controllers/page.js b/src/components/controllers/page.js new file mode 100644 index 000000000..72744473e --- /dev/null +++ b/src/components/controllers/page.js @@ -0,0 +1,14 @@ +import { useRoute } from 'preact-iso'; +import { navRoutes } from '../../lib/route-utils'; +import { NotFound } from './not-found'; +import { DocLayout } from './doc-page'; + +export function Page() { + const { path } = useRoute(); + + if (!navRoutes[path]) { + return ; + } + + return ; +} diff --git a/src/components/controllers/page/index.js b/src/components/controllers/page/index.js deleted file mode 100644 index 9c81244aa..000000000 --- a/src/components/controllers/page/index.js +++ /dev/null @@ -1,175 +0,0 @@ -import { useEffect, useMemo } from 'preact/hooks'; -import cx from '../../../lib/cx'; -import ContentRegion from '../../content-region'; -import config from '../../../config.json'; -import style from './style.module.css'; -import Footer from '../../footer'; -import Sidebar from './sidebar'; -import Hydrator from '../../../lib/hydrator'; -import EditThisPage from '../../edit-button'; -import { InjectPrerenderData } from '../../../lib/prerender-data'; -import { isDocPage } from '../../../lib/docs'; -import { useStore } from '../../store-adapter'; -import { AVAILABLE_DOCS } from '../../doc-version'; -import { Time } from '../../time'; -import { usePage, getContentId } from '../utils'; - -export default function Page({ route, prev, next }, ctx) { - const store = useStore(['url', 'lang', 'docVersion']); - const { loading, meta, content, html, current, isFallback } = usePage( - route, - store.state.lang - ); - const urlState = store.state; - const url = useMemo(() => urlState.url, [current]); - - const docsUrl = useMemo( - () => url.replace(/(v\d{1,2})/, `v${AVAILABLE_DOCS[0]}`), - [url] - ); - - const layout = `${meta.layout || 'default'}Layout`; - const name = getContentId(route); - - const isReady = !loading; - - // workaround: we toc in the store in order for to pick it up. - if (meta.toc && ctx.store.getState().toc !== meta.toc) { - ctx.store.setState({ - toc: meta.toc - }); - } - - // Note: - // "name" is the exact page ID from the URL - // "current" is the currently *displayed* page ID. - - const canEdit = current !== 'index'; - const isBlogArticle = current.startsWith('/blog/'); - const hasSidebar = meta.toc !== false && isDocPage(url); - - useEffect(() => { - if (location.hash) { - const anchor = document.querySelector(location.hash); - if (anchor) { - // Do not use scrollIntoView as it will cause - // the heading to be covered by the header - scrollTo({ top: anchor.offsetTop }); - } - } - }, [html]); - - return ( -
- -
- -
- {isDocPage(url) && +store.state.docVersion !== AVAILABLE_DOCS[0] && ( -
- You are viewing the documentation for an older version of Preact.{' '} - Switch to the current version → -
- )} - - {isBlogArticle && } - -
-
-
- -
- ); -} - -function BlogMeta({ meta }) { - return ( -
- {meta.date &&
- ); -} diff --git a/src/components/controllers/page/sidebar.js b/src/components/controllers/page/sidebar.js deleted file mode 100644 index 41ed32fdb..000000000 --- a/src/components/controllers/page/sidebar.js +++ /dev/null @@ -1,68 +0,0 @@ -import style from './sidebar.module.css'; -import DocVersion from './../../doc-version'; -import SidebarNav from './sidebar-nav'; -import { useCallback } from 'preact/hooks'; -import config from '../../../config.json'; -import { useStore } from '../../store-adapter'; -import { useOverlayToggle } from '../../../lib/toggle-overlay'; -import { getRouteName } from '../../header'; - -export default function Sidebar() { - const [open, setOpen] = useOverlayToggle(false); - const toggle = useCallback(() => setOpen(!open), [open]); - const close = useCallback(() => setOpen(false), []); - const { docVersion, lang } = useStore(['docVersion', 'lang']).state; - - // Get menu items for the current version of the docs (guides) - // TODO: allow multiple sections - config[meta.section] - const docNav = config.docs - .filter(item => { - // We know that nested routes are part of the same - // doc version, so we just need to check the first - // route. - if (item.routes) { - item = item.routes[0]; - } - - return item.path.indexOf(`/v${docVersion}`) > -1; - }) - .reduce((acc, item) => { - if (item.routes) { - acc.push({ - text: getRouteName(item, lang), - level: 2, - href: null, - routes: item.routes.map(nested => ({ - text: getRouteName(nested, lang), - level: 3, - href: nested.path - })) - }); - } else { - acc.push({ - text: getRouteName(item, lang), - level: 2, - href: item.path - }); - } - return acc; - }, []); - - // TODO: use URL match instead of .content - const guide = config.nav.filter(item => item.content === 'guide')[0]; - const sectionName = getRouteName(guide, lang); - - return ( -
- - -
- ); -} diff --git a/src/components/controllers/page/style.module.css b/src/components/controllers/style.module.css similarity index 87% rename from src/components/controllers/page/style.module.css rename to src/components/controllers/style.module.css index 8c2491d19..dfc89912c 100644 --- a/src/components/controllers/page/style.module.css +++ b/src/components/controllers/style.module.css @@ -7,7 +7,7 @@ margin-bottom: 4rem; } - content-region:not([name='index']) :global(.markup) { + content-region:not([name='/']) :global(.markup) { margin-top: 2.5rem; @media (max-width: 600px) { @@ -45,14 +45,6 @@ text-align: center; } -.blogMeta { - /* Negative bottom margin negates content-region's top margin */ - margin: 2.5rem auto -3rem; - padding: 0.5em 1rem 0.25rem; - width: 100%; - max-width: var(--content-width); -} - content-region h1 { padding: 0.5rem 1rem 0.25rem !important; font-weight: 200; @@ -64,10 +56,6 @@ content-region h1 { } } -.authors { - display: inline-block; -} - .inner { display: flex; flex-direction: column; diff --git a/src/components/controllers/tutorial/index.js b/src/components/controllers/tutorial/index.js index 10e55edb7..aca963b5d 100644 --- a/src/components/controllers/tutorial/index.js +++ b/src/components/controllers/tutorial/index.js @@ -8,19 +8,42 @@ import { useMemo, useCallback } from 'preact/hooks'; +import { useLocation, useRoute } from 'preact-iso'; import linkState from 'linkstate'; import cx from '../../../lib/cx'; import style from './style.module.css'; import { ErrorOverlay } from '../repl/error-overlay'; import { parseStackTrace } from '../repl/errors'; -import ContentRegion from '../../content-region'; import widgets from '../../widgets'; -import { usePage } from '../utils'; -import { useStore, storeCtx } from '../../store-adapter'; import { InjectPrerenderData } from '../../../lib/prerender-data'; -import { getContent } from '../../../lib/content'; +//import { getContent } from '../../../lib/content'; +import { useLanguage } from '../../../lib/i18n'; +import { useContent } from '../../../lib/use-resource'; +import { useTitle, useDescription } from '../utils'; import { Splitter } from '../../splitter'; import config from '../../../config.json'; +import { MarkdownRegion } from '../markdown-region.js'; + +/** + * @typedef SolutionContext + * @property {boolean} solved + * @property {(boolean) => void} setSolved + */ + +/** + * @type {import('preact').Context} + */ +const SolutionContext = createContext(/** @type {SolutionContext} */ ({})); + +export function SolutionProvider({ children }) { + const [solved, setSolved] = useState(false); + + return ( + + {children} + + ); +} const IS_PRERENDERING = typeof window === 'undefined'; @@ -43,7 +66,7 @@ export default class Tutorial extends Component { content = createRef(); runner = createRef(); - static contextType = storeCtx; + static contextType = SolutionContext; resultHandlers = new Set(); realmHandlers = new Set(); @@ -79,7 +102,7 @@ export default class Tutorial extends Component { 'repl-initial': '', 'repl-final': '' }); - this.context.setState({ solved: false }); + this.context.setSolved(false); } } @@ -138,9 +161,8 @@ export default class Tutorial extends Component { this.setState({ error: null }); }; - render({ route, step }, { loading, code, error }) { + render({ step }, { loading, code, error }) { const state = { - route, step, loading, code, @@ -158,7 +180,6 @@ export default class Tutorial extends Component { function TutorialView({ step, - route, loading, code, error, @@ -166,35 +187,45 @@ function TutorialView({ CodeEditor, clearError }) { - const content = useRef(); + const content = useRef(null); const tutorial = useContext(TutorialContext); const [showCodeOverride, toggleCode] = useReducer(s => !s, true); - const { lang, solved } = useStore(['lang', 'solved']).state; - const fullPath = route.path.replace(':step?', step || route.first); - const page = usePage({ path: fullPath }, lang); - const solvable = page && page.meta.solvable === true; - const hasCode = page && page.meta.code !== false && step && step !== 'index'; + const { url } = useLocation(); + const { params } = useRoute(); + const [lang] = useLanguage(); + const { solved } = useContext(SolutionContext); + + const { html, meta } = useContent([ + lang, + !params.step ? 'tutorial/index' : url + ]); + useTitle(meta.title); + useDescription(meta.description); + + const solvable = meta.solvable === true; + const hasCode = meta.code !== false && step && step !== 'index'; const showCode = showCodeOverride && hasCode; - loading = - !page.html || (showCode && (!!page.loading || !Runner || !CodeEditor)); - const initialLoad = !page.html || !Runner || !CodeEditor; + loading = !html || (showCode && (!Runner || !CodeEditor)); + const initialLoad = !html || !Runner || !CodeEditor; // Scroll to the top after loading useEffect(() => { if (!loading && !initialLoad) { content.current.scrollTo(0, 0); } - }, [fullPath, loading, initialLoad]); + }, [url, loading, initialLoad]); // Preload the next chapter - useEffect(() => { - if (page.meta && page.meta.next) { - getContent([lang, page.meta.next]); - } - }, [page.meta && page.meta.next, fullPath]); + // TODO: Webpack creates a circular dependency that + // it cannot resolve. Temporarily disabled + //useEffect(() => { + // if (meta && meta.next) { + // getContent([lang, meta.next]); + // } + //}, [meta && meta.next, url]); const reRun = useCallback(() => { let code = tutorial.state.code; @@ -206,7 +237,6 @@ function TutorialView({ return ( {!initialLoad && (
- @@ -313,10 +340,10 @@ function TutorialView({ @@ -335,7 +362,6 @@ const REPL_CSS = ` function ReplWrapper({ loading, - subtleLoading, solvable, solved, initialLoad, @@ -344,7 +370,7 @@ function ReplWrapper({ }) { return (
- +
tutorial.runner.current.realm.globalThis._require(m); const fn = new Function( @@ -419,11 +445,10 @@ function TutorialSetupBlock({ code }) { 'useEffect', 'useRef', 'useMemo', - 'useStore', 'useResult', 'useRealm', 'useError', - 'store', + 'solutionCtx', 'realm', 'require', code @@ -437,11 +462,10 @@ function TutorialSetupBlock({ code }) { useEffect, useRef, useMemo, - useStore, tutorial.useResult, tutorial.useRealm, tutorial.useError, - store, + solutionCtx, tutorial.runner.current && tutorial.runner.current.realm, require ); @@ -454,8 +478,8 @@ function TutorialSetupBlock({ code }) { /** Shows a solution banner when the chapter is solved */ function Solution({ children }) { - const { solved } = useStore(['solved']).state; - const ref = useRef(); + const { solved } = useContext(SolutionContext); + const ref = useRef(null); useEffect(() => { if (solved) { diff --git a/src/components/controllers/utils.js b/src/components/controllers/utils.js index 5eb2663e6..288451e16 100644 --- a/src/components/controllers/utils.js +++ b/src/components/controllers/utils.js @@ -1,7 +1,5 @@ -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useEffect } from 'preact/hooks'; -import { getContentOnServer, getContent } from '../../lib/content'; -import { getPrerenderData } from '../../lib/prerender-data'; import { createTitle } from '../../lib/page-title'; /** @@ -28,86 +26,3 @@ export function useDescription(text) { } }, [text]); } - -export const getContentId = route => route.content || route.path; -export function usePage(route, lang) { - // on the server, pass data down through the tree to avoid repeated FS lookups - if (PRERENDER) { - const { content, html, meta } = getContentOnServer(route.path, lang); - return { - current: getContentId(route), - content, - html, - meta, - loading: true // this is important since the client will initialize in a loading state. - }; - } - - const [current, setCurrent] = useState(getContentId(route)); - - const bootData = getPrerenderData(current); - - const [hydrated, setHydrated] = useState(!!bootData); - const [content, setContent] = useState( - hydrated && bootData && bootData.content - ); - const [html, setHtml] = useState(hydrated && bootData && bootData.html); - - const [loading, setLoading] = useState(true); - const [isFallback, setFallback] = useState(false); - let [meta, setMeta] = useState(hydrated ? bootData.meta : undefined); - if (!meta) meta = (hydrated && bootData.meta) || {}; - - const lock = useRef(); - useEffect(() => { - if (!didLoad) { - setLoading(true); - } - const contentId = getContentId(route); - lock.current = contentId; - getContent([lang, contentId]).then(data => { - // Discard old load events - if (lock.current !== contentId) return; - onLoad(data); - }); - }, [getContentId(route), lang]); - - useTitle(meta.title); - useDescription(meta.description); - - let didLoad = false; - function onLoad(data) { - const { content, html, meta, fallback } = data; - didLoad = true; - - // Don't show loader forever in case of an error - if (!meta) return; - - setContent(content); - setMeta(meta); - setHtml(html); - setLoading(false); - setFallback(fallback); - const current = getContentId(route); - const bootData = getPrerenderData(current); - setHydrated(!!bootData); - setCurrent(current); - // content was loaded. if this was a forward route transition, animate back to top - if (window.nextStateToTop) { - window.nextStateToTop = false; - scrollTo({ - top: 0, - left: 0 - }); - } - } - - return { - current, - content, - html, - meta, - loading, - isFallback - }; -} diff --git a/src/components/doc-version/index.js b/src/components/doc-version/index.js index 0399e251a..cbb9804bf 100644 --- a/src/components/doc-version/index.js +++ b/src/components/doc-version/index.js @@ -1,11 +1,7 @@ +import { useCallback } from 'preact/hooks'; +import { useLocation, useRoute } from 'preact-iso'; +import config from '../../config.json'; import style from './style.module.css'; -import { getCurrentUrl, route } from 'preact-router'; -import { useStore } from '../store-adapter'; - -function onChange(e) { - const url = getCurrentUrl().replace(/(v\d{1,2})/, `v${e.target.value}`); - route(url); -} export const AVAILABLE_DOCS = [10, 8]; @@ -13,16 +9,28 @@ export const AVAILABLE_DOCS = [10, 8]; * Select box to switch the currently displayed docs version */ export default function DocVersion() { - const { docVersion } = useStore(['docVersion']).state; + const { path, route } = useLocation(); + const { version, name } = useRoute().params; + + const onChange = useCallback( + e => { + const version = e.currentTarget.value; + const url = config.docs[version]?.[name] + ? path.replace(/(v\d{1,2})/, version) + : `/guide/${version}/getting-started`; + route(url); + }, + [path, route] + ); return (