{"id":20182184,"url":"https://github.com/nichoth/flobz","last_synced_at":"2025-05-07T03:30:34.178Z","repository":{"id":77400036,"uuid":"355514139","full_name":"nichoth/flobz","owner":"nichoth","description":null,"archived":true,"fork":false,"pushed_at":"2021-08-07T01:45:34.000Z","size":1092,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-03T06:13:14.794Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nichoth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-07T11:09:26.000Z","updated_at":"2024-10-06T05:27:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"885b8594-8206-43a5-8195-8fe2cbe092d7","html_url":"https://github.com/nichoth/flobz","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fflobz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fflobz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fflobz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fflobz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nichoth","download_url":"https://codeload.github.com/nichoth/flobz/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252806322,"owners_count":21807188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T02:37:53.319Z","updated_at":"2025-05-07T03:30:34.158Z","avatar_url":"https://github.com/nichoth.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flobz\n\nhttps://flobz.netlify.app/\n\n[![Netlify Status](https://api.netlify.com/api/v1/badges/5c0cc90e-7806-455e-9444-6fc5705baa00/deploy-status)](https://app.netlify.com/sites/flobz/deploys)\n\nA naive client side routing example\n\nwe want to have a static website generated, but with every page having\nsome JS that will take over the routing \u0026 rendering\n\n---------------------------------------\n\n## build\n```\nnpm run build\n```\n\n\n--------------------------------------\n\nCould use the router object to pre-render every route that you add\n\nthen the `bundle.js` file would call `hydrate()` instead of `render`\n\n--------------------------------------------\n\nhttps://medium.com/@luke_schmuke/how-we-achieved-the-best-web-performance-with-partial-hydration-20fab9c808d5\n\nThis is an example of sending the same static content for all routes. Would not scale that well if there is a lot of content. However, it might work as a pwa type application, since it gets installed \u0026 cached via a service-worker\n\nHow to render each route on the server, ahead of time, then serve the right\ncontent per route, and hydrate things once it's downloaded? Hydration is \nimportant, because the routes are handled client-side. Each route would\nneed to request its content when you load the route, so that way it saves you from downloading all content up-front.\n\n----------------------------------------------------------------------\n\n* [hydrate() preact docs](https://preactjs.com/guide/v10/api-reference/#hydrate)\n* [htm-ssr.js](https://gist.github.com/developit/699c8d8f180a1e4eed58167f9c6711be)\n\n---------------------------------------------------\n\nWe render all routes as static pages, and also include js code for \nbrowser-side routing in the application. That way it is the best of both\nworlds -- fast initial page load, plus fast route changes or css\ntransitions on subsquent route changes.\n\n--------------------------------\n\n`ruta3` has a property [`.routes`](https://github.com/bevacqua/ruta3/blob/master/index.js#L66) that is the list of routes that we need.\n\n------------------------------------------\n\n```\nnode --experimental-specifier-resolution=node ./src/node-version.js \n```\n\nWe had to do some stuff to adapt this to `import` style:\n```js\nimport path from 'path';\nconst __dirname = path.dirname(new URL(import.meta.url).pathname);\nimport { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\n```\n\n-------------------------------------\n\n* https://medium.com/dev-channel/a-netflix-web-performance-case-study-c0bcde26a9d9\n* [htm-ssr.js](https://gist.github.com/developit/699c8d8f180a1e4eed58167f9c6711be)\n* https://preactjs.com/guide/v10/api-reference/#hydrate\n\n\n----------------------------------------------\n\n\n* https://preactjs.com/cli/pre-rendering/\n\nwhich routes are prerendered\n\u003e Out of the box only the homepage is pre-rendered. In order to pre-render additional URLs (routes), you'll want to add a prerender-urls.json file to your project. \n\nJust request the *JSON data* on client-side routing event --\n\n\u003e When a visitor first navigates to your app, the markup will contain only pre-rendered data for that specific page to avoid unnecessary download size. When they navigate to another route via **client-side navigation**, there won't be inlined pre-render data for that page. To get the data, make a request to `/\u003cnew-route\u003e/ preact_prerender_data.json` to get the data that route. Preact CLI enables this by generating a preact_prerender_data.json file next to each pre-rendered page at build time.\n\nThis is the same as what we have done in this example. Hydrate the first time,\ndownload and render subsequent pages. But we are doing it ad-hoc, without a prerendering component\n\n\n`@preact/prerender-data-provider` -- It either includes the inlined data, or fetches from JSON from a URL\n\n\n## todo\n* look at [unistore](https://github.com/developit/unistore)\n* look at [stockroom](https://github.com/developit/stockroom)\n\n\n----------------------------------------------\n\n## netlify CMS\n\n* add an `admin` folder to the site root:\n```\nadmin\n ├ index.html\n └ config.yml\n```\n\n* setup CMS configuration\n* add authentication on netlify UI\n* add the netlify identity script in two places, the main index html file,\nand the `admin/index.html`:\n\n```\n\u003cscript src=\"https://identity.netlify.com/v1/netlify-identity-widget.js\"\u003e\u003c/script\u003e\n```\n\n* add a redirecting script to `index.html`:\n```html\n\u003cscript\u003e\n  if (window.netlifyIdentity) {\n    window.netlifyIdentity.on(\"init\", user =\u003e {\n      if (!user) {\n        window.netlifyIdentity.on(\"login\", () =\u003e {\n          document.location.href = \"/admin/\";\n        });\n      }\n    });\n  }\n\u003c/script\u003e\n```\n\n--------------------------------------------------\n\nBe sure to use a folder that is hosted publicly for the storage of post source\ncontent: \n\n```yml\ncollections:\n  - name: \"blog\" # Used in routes, e.g., /admin/collections/blog\n    label: \"Blog\" # Used in the UI\n    folder: \"public/_posts/blog\" # The path to the folder where the documents are stored\n```\n\nWe will use this `folder` path later when we fetch content\n\npost content has a URL like this:\n```\nhttps://flobz.netlify.app/_posts/blog/2021-04-10-title-3.md\n```\n\n## TODO\n* loop through the post names and create a link to each in the home page\n* for browserified version, could create a JSON file with the file names, then\n  require the json file in the app, and render with a link to each file\n* the page for a post needs to fetch the content when the page loads\n* handle routes with wildcards in them. The pages built should come from\n  the CMS generated source files, not the routes added to the router.\n* in the front-end version, need to request the content from the src file --\n  `/_posts/post-name`\n\n---------------------------------------------\n\nneed some kind of convention when you start a website for how the file names\n/ routes will work\n\n\nThe filenames in `/public/_posts/blog` will be used in the actual routes,\nso be sure to name the files well.\n\n----------------------------------------------\n\nWe can't use code in both browserify and node anymore\n\nin `src/routes.js` there are these 2 lines:\n```js\nimport { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\n```\n\nThey make things work in node, but it breaks it for browserify\n\n---------------------------------------------\n\nNeed to get a JSON file with a list of routes for the nav\n\nNeed to build all the pages/routes for the files\n\n\n\n\n\n---------------------------------------------------------------\n\n\n* [developit/htm-ssr.js](https://gist.github.com/developit/699c8d8f180a1e4eed58167f9c6711be)\n\n* [Build a SSR App With Preact, Unistore, and Preact Router](https://www.digitalocean.com/community/tutorials/build-a-ssr-app-with-preact-unistore-and-preact-router)\n\n\u003e To summarize, the idea is to initially render the app on the server first and then render the components on the browser.\n\n\n## it actually works\n\n### the situation\nSo you have a website, and there is too much content to make a single page app. You want the website to be fast, and also be SEO'd. That means we can make a static website for the SEO part, and static sites are pretty fast too. \n\nBut it would be a *little bit faster* if once you loaded the first page on the site, each subsequent page that you visited requested *just the content* for that page, instead of full markup. That way you could re-use redundant parts like navigation elements (things that appear on every page).\n\nThat's what this is. It is a real thing and it is hosted on netlify -- [https://flobz.netlify.app/](https://flobz.netlify.app/)\n\n---------------------------------------\n\nIn netlify, create the site and [add netlify-cms](https://www.netlifycms.org/docs/add-to-your-site/). \n\nIn the `/admin/config.yml` file, make sure that new posts are created in the `/public` folder. This is important because we want to request the `md` content whenever we navigate within the site.\n\n```yml\n  folder: \"public/_posts/blog\" \n```\n\nThere are two important scripts here. One is a node file that will generate a static site, and the other is the entry file for the website.\n\nIn the node file, we want to read all our content files, generate a page with them, and write the page to the right path in the site.\n\nFor every source file in the site, we match it against the router, and write the result to the filesystem\n\n```js\nimport router from './routes'\nvar hyperstream = require('hyperstream')\n\nfs.readdir(__dirname + '/../public/_posts/blog', (err, files) =\u003e {\n  files.forEach(fileName =\u003e {\n    var m = router.match('/posts/' + path.basename(fileName, '.md'))\n    var { view, getContent } = m.action(m)\n\n    getContent().then(content =\u003e {\n      var el = html`\u003c${shell} active=${path.basename(fileName, '.md')}\n          links=${_files}\n      \u003e\n          \u003c${view} content=${content} /\u003e\n      \u003c//\u003e`\n\n      mkdirp(dirPath).then(() =\u003e {\n        var hs = hyperstream({\n            '#content': {\n                _appendHtml: renderToString(el)\n            }\n        })\n\n        var rs = fs.createReadStream(__dirname + '/index.html');\n        var ws = fs.createWriteStream(indexPath)\n        rs.pipe(hs).pipe(ws)\n      })\n    })\n  })\n})\n```\n\nAlso during this stage, be sure to write a json file with all the routes in it. We can use that file to generate the navigation in the front end site.\n\n```js\nvar _files = files.map(file =\u003e path.basename(file, '.md'))\nfs.writeFileSync(__dirname + '/links.json', JSON.stringify(_files))\n```\n\nWe pass in the `links` prop when rendering the preact component\n```js\nvar el = html`\u003c${shell} active=${path.basename(fileName, '.md')}\n    links=${_files}\n\u003e\n```\n\nIn the frontend JS file, we want to use the same router. We should be able to just import the same file in both places, but [a bug](https://github.com/mattdesl/esmify/issues/15) prevents this at the moment. So there is a file `routes-browser.js` that has the same routes as the backend, but we are using the `fetch` function to request the source `.md` content instead of reading from `fs`.\n\n-----------------------------\n\nSo what happens is that the first time you load the site in a browser, it will download and display the static webpage. So you see the content asap. Then we `hydrate` the content with preact. \n\nThen every time you navigate via the page's links, we will request the `.md` content for that route, and re-render the site using preact. So it is as quick as possible, and also SEO.\n\nIt nice to independently come to the same conclusion as [an internet article](https://www.digitalocean.com/community/tutorials/build-a-ssr-app-with-preact-unistore-and-preact-router) --\n\n\u003e To summarize, the idea is to initially render the app on the server first and then render the components on the browser.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichoth%2Fflobz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnichoth%2Fflobz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichoth%2Fflobz/lists"}