Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Suggestions in refactor #5

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

renoirb
Copy link

@renoirb renoirb commented Feb 17, 2023

Warning I'm aware it's not working ATM. I'm going to shave out things from my fork that can be ported back and I probably made this PR too much in a hurry. I'll remove "draft" once what I'm sharing here doesn't break.


Hi, I'm using your work and am seeing things that I could share back. I'm keeping them here in this PR. Do what you want with them.

I'm implementing slowly my own fork in https://github.com/renoirb/blogtini based off your amazing work! I'll be using customElements, and see some perf tweaks and other things. Also, I have bad working memory, it helps me making small things, separate concerns. I'll probably make tests etc.

Do what you want, I'll try to give back from here. If you want it.

js/blogtini.js Show resolved Hide resolved
@traceypooh
Copy link
Owner

Thanks for liking and diving in!
this is a lot to consider already, so am going to throw off a few initial thoughts...

You happened to have already seen the (optional) config.yml at the root of the top of the blog/site project, right?
that's an excellent place for customizations (like docroot changes, etc.) and am hoping to move more and more there, as needed.

I had envisioned this as being ideally a two part system:

  1. JS to "crawl" your sitemap (or RSS feed, have work uncommitted on that), build up tags/categories maps, sort posts, etc...
  2. A completely pluggable "theme" in JS. so if one doesn't like/prefer the current based-entirely-on hugo-futurue-imperfect-slim theme (which I've already lightly hacked at my current hugo site since it went unsupported, grrr.....) https://poohbot.com -- then one could just plug in a new theme.js file "pointer" to some other setup and UI/UX.

SEO is a challenge. I feel i'm at war with google and super pissed they get to decide everything and get to player hate SPA sites that want to entirely build up the <head> dynamically with JS. Some of the meta and/title tags get through, but a lot comes down to how much of their JS-enabled crawler "budget" (w/ timeout limits to how much JS and loading they'll do) they'll let you do. Still, I am hopeful some day if this and similar projects take off, they'll listen more in the future. Meanwhile, their JS crawlers should get better, faster, more efficient and more common. TBD!

I view any .ts (absolutely) or .mjs (potentially) extensions as a dealbreaker -- at least, the project goal is to only support modern browser native capabilities. I'm on the fence about types in JS -- in general -- but I have 0 interest in anything that's not browser native. (just FYI).

Sidenote: I originally aimed to have a jekyll mod to enable us to use their basic setup. I have a fork & working branch, but it's kind of been months of crickets over there for the final config/testing questions... In the end, I haven't been sure any build step is appropriate (the hope is none is needed -- though you may have noticed their is an optional build step to run before commit/push (of say a new post to a blog) that rebuilds/updates the sitemap.xml (and/or RSS feed files) w/ a simple shell script and git hook. Mostly, I wanted a static markdown site that had no build steps and JS only as the guiding goal. As the founding & longest coder at https://archive.org -- I have keen interest in "roach proof" (when the world nukes away and only the roaches and alien visitors are left) blog pages. So they'll find the raw files w/ a browser and be like "ooh! i know what this is!" and it "just works" 1000s of years from now :D

RE: custom elements...
So my work front-end UI/UX centered team at work uses and prefers lit -- have you happened to have seen that?
I'm a bit on the fence about it for this project since the pages are generally quite static in reality (this whole thing was born of ideas based of static site generators and their pluses and minuses). I'm curious where you're thinking custom elements / web components / lit might be worth the effort?

OK! that's some good initial thoughts! Please don't take any of them in a discouraging light -- none of them are meant to be so, and I want to reiterate that I'm thrilled you are interested and active in this -- this was my hope for this project -- to get a team of folks involved and using it :)

@traceypooh
Copy link
Owner

Oh, and re: .mjs files... the entire project is type="module" by default (I'll never use CJS again, since ESM is so soooooo good ( the best import system setup on the planet, across all languages, hands down :D)).

So you can just rename any .mjs file to .js to avoid any (potential) browser confusion.

@renoirb
Copy link
Author

renoirb commented Feb 21, 2023

(writing this from a phone, beside a sleeping baby. Apologies for typos)

Hi Tracey,

I got interested because I also think about long term ("roach proof"), leverage the (web) platform™, browser native ("Don't break the web","Cool URIs don't change", etc.), and maintainability. From what you've written, I feel like I am actually on the same page as you.

I am more efficient programmer when I separate complexity in smaller units with tests and isolation. My working memory is very bad (all the time) and that's a necessity for me to produce anything functional.

I'm willing to contribute with pieces that can be contributed into how you're planning it. I may make different choices on my own site, but the ideas I have in mind align almost totally. (I say almost, because typings is to me a neat model to design code before implementation, and, of course, you might have other ideas I can't guess that are missing. I like being precise and I'm now getting pedantic)

(rest inline)

Thanks for liking and diving in! this is a lot to consider already,

Yup.

I'm an "exploratory programmer". My fork (renoirb/blogtini) is an orphan fork, just getting the minimal and experiment. As I'm trying say, I'd like to separate concerns and continue on your ideas since they align with mine.

so am going to throw off a few initial thoughts...

Thanks for all this write up.

Your talk blew my mind and shown me running an idea I've had for years, but couldn't work on.

I'm glad you've done this. As I'm trying to tell, I'd love to help.

You happened to have already seen the (optional) config.yml at the root of the top of the blog/site project, right? that's an excellent place for customizations (like docroot changes, etc.) and am hoping to move more and more there, as needed.

Yup, saw all that.

it's clever to use browser's treatment of an .html file with empty body directly with markdown so browser treats as content-type: text/html. Like Nicholas Zakas said when introducing Progressive Enhancement, an automatic escalator, at worse case, is still an escalator.

It's cool, but has dome drawbacks. SEO being one, it'd be best that inter-page be HTML a elements (which is valid Markdown). Some other markup maybe. Like the W3C does with Bikeshed and Publican. Also other drawbacks for internationalization.

I had envisioned this as being ideally a two part system:

  1. JS to "crawl" your sitemap (or RSS feed, have work uncommitted on that), build up tags/categories maps, sort posts, etc...

Yup. Got that.

Also using sitemap.xml as input. Clever stuff.

Maybe I'd try to use IndexedDB and see how we could leverage it for search, and also, maintain its state. I'm unsure if IndexedDB is appropriate for text fuzzy search. But I'm curious if we have heavy content, how it would do. We could import the whole WebPlatform docs markdown content and see how speed goes. See this article I've written about that, it's about 5000 markdown files. Have you tried with that many?

As for the search index, tags, categories indices, we could probe using Last-Modified response header, and sometimes call, and maintain index freshness without hammering needlessly.

GET /foo HTTP/1.1
If-Modified-Since: ...
Accept: text/html

304 Not Modified

So we know we don't need to update.

Something like that. Keep the response parsing the same, it's simple if it's Markdown, no DOM to create and walk. Then "re calculate" the indices when changes.

  1. A completely pluggable "theme" in JS. so if one doesn't like/prefer the current based-entirely-on hugo-futurue-imperfect-slim theme (which I've already lightly hacked at my current hugo site since it went unsupported, grrr.....)

Yup. Got that too.

I would love to allow people to use as-is, and also be extensible. That's why the crawling (indexing, etc) and rendering feels like should be separated. That's what I'm experimenting with at the moment.

And web components, dependency less (just use bare metal native DOM). Because I'd love it to be the smallest as possible.

https://poohbot.com -- then one could just plug in a new theme.js file "pointer" to some other setup and UI/UX.

That entry point /theme.js is great. Also the perfect place to tell whether the file is loaded from localhost, or file:///... and work.

Staticman is cool too, thanks for sharing. I was thinking if it would be applicable to make comments be stored in JSON similar to ActivityStream comments, in some schema so rendering them be the same as rendering the site's own activities. I'm aware that a person's POST /inbox (if there was) be the ideal than static comment. Basically a local filesystem cache of comments as if the commenter did have its own. I'm curious about Annotations on pages as ways to accept comments. But I can go in too many directions.

Yeah. I should table that for now. I'm going into a tangent. Basically find a way to use what I'm mentioning, done right, so we store comments via PRs created by a bot that staticman does. If we use similar storage format as a filesystem cache ActivityStream, we'd get "two birds, one stone" (poor birds, gotta find cruelty free image for this expression someday). And it's web native (!)

SEO is a challenge. I feel i'm at war with google and super pissed they get to decide everything and get to player hate SPA sites that want to entirely build up the <head> dynamically with JS. Some of the meta and/title tags get through, but a lot comes down to how much of their JS-enabled crawler "budget" (w/ timeout limits to how much JS and loading they'll do) they'll let you do. Still, I am hopeful some day if this and similar projects take off, they'll listen more in the future. Meanwhile, their JS crawlers should get better, faster, more efficient and more common. TBD!

Yes.

That's why, we might have to do like what they do in W3C's specs for essential things like links we care about to be done by hand, not markdown.

I view any .ts (absolutely)

I love how TypeScript helps me design. But, that code is for writing and maintaining. What's deployed and running, least dependencies. Native. I agree.

or .mjs (potentially) extensions as a dealbreaker

No problem, I used .mjs thinking it was the future proof way to tell its ESM served over http.

-- at least, the project goal is to only support modern browser native capabilities. I'm on the fence about types in JS -- in general -- but I have 0 interest in anything that's not browser native. (just FYI).

On the same page.

Sidenote: I originally aimed to have a jekyll mod to enable us to use their basic setup. I have a fork & working branch, but it's kind of been months of crickets over there for the final config/testing questions... In the end, I haven't been sure any build step is appropriate (the hope is none is needed -- though you may have noticed their is an optional build step to run before commit/push (of say a new post to a blog) that rebuilds/updates the sitemap.xml (and/or RSS feed files) w/ a simple shell script and git hook. Mostly, I wanted a static markdown site that had no build steps and JS only as the guiding goal.

I agree. The smallest amount of boilerplate and leverage what the browser does by design. Starting the response body with "---" and markdown as an .html file, the browser treats as if we did have <html>...

Unfortunately, as a native French canadian, being constantly faced to the fact that our content isn't in English and how the other language doesn't play well (screen reader being one "victim"), we lose the opportunity to tell sans JavaScript the html[lang] of ce que nous écrivons

As the founding & longest coder at https://archive.org -- I have keen interest in "roach proof" (when the world nukes away and only the roaches and alien visitors are left) blog pages.

I love your perspective. I'm interested to learn more about what you've learned.

So they'll find the raw files w/ a browser and be like "ooh! i know what this is!" and it "just works" 1000s of years from now :D

Yes. I'm also such type of nerd. Had cool convos with former colleagues when I was SysAdmin on WebPlatform.org as a W3C team members. I learned so much about how it all started, how things are maintained there. It's constantly in my mind. Long term, and to understand what and how to properly leverage technology

RE: custom elements... So my work front-end UI/UX centered team at work uses and prefers lit -- have you happened to have seen that?

Yes. I also participated in the Web Component CG Community Protocols. The "context API". I contributed to reviewing Lit.dev's Context protocol lit/lit#1955 (daisy-chaining always annoyed me, that Protocol solves it nicely)

In my fork (d.g. this broken looking page, try the search, look for "html", notice the date of article. It's updated via a context-request event. Code to request, and code to update

I have good understanding of Vue, and Lit.dev internals. My hobby is to actually compare how frameworks runtime does the same effect. I love Vue 3's way of describing render trees, how we can use raw JavaScript to describe how to generate HTML so that most use case (and mostly everyone uses template syntax) but for real hairy patterns, native 1-1 parity render tree.

Lit doesn't have that lime Vue does. But I like how it's a middle ground.

I also have the ambition to achieve to load least code should a view need only the minimum. Which is a contrast to what most people do by bundling everything in one payload. ESM import allow to dynamically load when needed. Maybe a view only load /theme.js, the site's index is still warm, no check to make, load the "app layout". Done. 2 JavaScript files loaded parsed. Ideally the least amount of DOM mutations.

See a prototype of the app layout live here: https://renoirb.github.io/blogtini/using-app-layout.html

I also worked in ways to package for reuse of that "chrome" (repetitive UI markup) with Vue https://youtu.be/C39eZYD-7Ow so you import, tell the navigation tree, listen to events. So the same package is reused between projects.

I like https://lit.dev, but it's also possible to be framework naked. Or Svelte, generating code that doesn't require loading a framework runtime.

If you look at the bookmarks in my fork, the ContextAPI, you'll see how we can update parts of the view without any "reactivity" or "stream", or ... runtime. Just native DOM events.

I serve some of it from my domain name, an attempt to mimick Deno's way of publishing package for web browser runtime. And I was thinking to port lit-labs/context package.

I'll stop here for now. I've added more for you to parse.

I said I would stop and I didn't. I wouldn't mind having a vocal video conf call.

My plan is to continue experimenting, if you wouldn't have done this, I would probablly have made the same choices. I'm trying to go in a similar place as what you've described. Also, I'm a new stay at home dad. Web nerd. I can hack when my 7 months old son naps. The Web is constantly in my mind since 2001 (professionally). So, your project got my attention. I wanna contribute.

Cheers.

@renoirb
Copy link
Author

renoirb commented Feb 21, 2023

Oh, and re: .mjs files... the entire project is type="module" by default (I'll never use CJS again, since ESM is so soooooo good ( the best import system setup on the planet, across all languages, hands down :D)).

So you can just rename any .mjs file to .js to avoid any (potential) browser confusion.

Exactly. Same feeling.

Now, the question is how to write small packages with testing, publishing for the same code, different targets. Different releases, published as versioned URLs, yet code of a module using it refer to module with version less, but specified as an importMap.

I've been contributing and using RushJS.io for that. I've been asking about that today on RushJS' community chat on Zulip. They responded that they have an issue for that use-case microsoft/rushstack#3934. But all that is probably too bleeding edge though.

@traceypooh
Copy link
Owner

re: your first long reply (esp.)

I. Love. This.

Minor suggested alteration.

Let's setup a standing meeting to checkin & collaborate! 🙌🏻

Hehe. Well, we can see, but I think that could be a great way to coordinate & plan.

Copy link
Author

@renoirb renoirb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Please consider this notes for when we meet)

There are other things still in progress

const state = {
top_dir: SITE_ROOT_BASE_URL,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is suggested (SITE_ROOT_BASE_URL, maybe not use "top_dir") so that what's /theme.js can know what's the baseUrl, if it's file:/// it'll be different than on an http:// with a / or /subfolder like GitHub pages often lets us do.

Comment on lines +293 to +300
/**
* Pick all content of the page, let's figure out what to do with it.
* Beware, if we use innerHTML and want the front-matter not to be with escaped entities
* we got to use textContent instead.
*/
const textContent = document.getElementsByTagName('body')[0].textContent
const [raw_fm] = splitFrontMatterAndMarkdown(textContent)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed that innerHTML converts things into htmlentities (e.g. YAML with &). Also, maybe make splitFrontMatterAndMarkdown just do that: split the page (not shown in this PR)

Comment on lines +310 to +314
const base = my_frontmatter?.base ?? SITE_ROOT_BASE_URL
const href = window.location.href
const maybe = href.replace(base ?? '', '')

state.pathrel = state.is_homepage ? '' : '../' // xxxx generalize
state.pathrel = state.is_homepage ? '' : maybe // xxxx generalize
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things around here are hard to figure out, I would suggest to simplify (what I was trying to understand with maybe) that or organize them differently.

Comment on lines +360 to +361
const blogtiniMain = createBlogtiniStuffWrapper(document, 'blogtini-main')
blogtiniMain.innerHTML = `<!-- RBx htmlString main BEGIN -->
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using innerHTML, we can use document.createElement('template').innerHTML and clone and then appendChild. Otherwise (especially when using customElements) it mounts components more than once because of re-creating. It was a head scratcher why I would have the double of components in the console.log to realize that the re-creation effectively drops and re-created the same.


// eslint-disable-next-line no-use-before-define
await parse_posts(file2markdown)
await parse_posts(documentResponseObjects)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got surprised to realize that those are a collection of HTTP Response objects.

* What is the intented URL to expose this site, it's most likely over http, not "file://",
* and possibly not at the root of the domain name.
*/
const PRODUCTION_SITE_ROOT_BASE_URL = 'https://renoirb.github.io/blogtini/'
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare what's your intended production location. If it's not that, it's for dev. It's at the root (/theme.js) and it gets passed at the import below.

* This should work for file:/// URLs
* (assuming it's supported, Safari does, not Chromium thus far 2023-02-03)
*/
const SITE_ROOT_BASE_URL = await import.meta.url?.replace('theme.js', '') // await import.meta.resolve('./theme.js', import.meta.url) // ??'').replace('theme.js', '');
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare what's the current URL thanks to import.meta.url, if it's not the same as PRODUCTION_SITE_ROOT_BASE_URL it must be development.

Comment on lines +30 to +36
import(
`./js/blogtini.js?SITE_ROOT_BASE_URL=${encodeURIComponent(
SITE_ROOT_BASE_URL,
)}&PRODUCTION_SITE_ROOT_BASE_URL=${encodeURIComponent(
PRODUCTION_SITE_ROOT_BASE_URL,
)}`
)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tell where we are, and what's the production URL without making people using blogtini change the code there.


cleanUpInitialPayloadMarkup(document)
Copy link
Author

@renoirb renoirb Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to avoid what's happening a few lines below. To trash everything on the DOM, instead see how to clean up js/utils cleanUpInitialPayloadMarkup, I thought that we should keep the initial Markdown but hide it.

@renoirb
Copy link
Author

renoirb commented Feb 23, 2023

(Please consider this notes for when we meet. Apologies for the apparent chaos, that's my brain.)

Instead of using Lit, we could make a thin wrapper (html tagged function) like what Enhance does.

Notice the example in the docs

export default function MyHeader({ html }) {
  return html`
    <header>
      <h1>Header</h1>
      <nav>
        <a href=/>home</a>
        <a href=/about>about</a>
      </nav>
    </header>
  `
}

Is possible because of this

I have other ideas, but the more we add the more it grows. E.g. what Plume on a Solid pod is, or that Enhance framework, or singleSpa.

Priorities could be to

  • focus on native,
    • a[is="augmented-anchor] to extend progressively anchors. for local important links. CSS or JS broken, it's still a link
    • pre[is="code-block][title] of some sort to support making one or more code blocs with tabs to switch between them if they're next to each other. Optionally plug something to do the code hilight, the component emit a context-request event, it's a code block, take the code, colorize, replace the innerHTML. If, no CSS or JS. It's pre-formatted text
  • lowest amount of dependencies
  • focus on the fetch pages, manage tags, categories,
    • store in static files and/or store on IndexedDB, support query either one of them.
    • A "build" step to generate the indexes, walk the files to be run either client or server side, the "build" being run from CI. Also, separate Markdown parsing pluggable (say a page is for talk slides, we don't want it HTMLized.
    • would be nice hook for sending to a Solr/ElasticSearch making lunr a simple example but not core
  • Provide a markup (customElements with slots and attributes as interface for extension when using)
    • with part to allow styling from outside. Provide basic CSS style,
    • have separate "theme", leverage Cascade and show young'ings how to leverage that cascade we were doing back when OOCSS was an "ah ha!"
    • Have ContextAPI data shapes events (like date, then N formatted variants resolved by it) so core doesn't depend on dayjs, but a distribution of blogtini as example using it. Other thpes of data could be resolved that way. Tags or images loaded from different URL after being loaded (e.g. img[src="~/img.png] to a bucket somewhere)
    • provide nice utility components like a a[data-archive-pull][href] that would be a trigger for a ContextAPI to tell archive.org dates elsewhere in the content (a file somewhere we could tell which URLs we already know date strings) and adapt that a to add icon, and add a list of links to those.
    • Other custom elements for other purposes like displaying page edit history from GitHub, GitLab.
  • That one might add the need for a different way of counting pages in than sitemap.xml
    • Support displaying tweets, statuses stored the same way as the rest. /statuses/twitter/0000/index.html and a custom element or different layout to display the data. Same for toots
    • Personal page archives (not to plug a project of mine, but). A list of canonical copies of web pages kept because I love referring back to a great article. For example, recipes cookbook stored in /archive//yupitsvegan.com/caramelized-onion-hummus/ (preview, look the attribution note at the bottom), stored as markdown (more at archivator-demo) they shouldn't be looking like my site, they're canonical copies. Also, maybe I'd love to add an Annotator on those pages
  • Make HTML views as "micro-frontend" that gets included by composition
    • commenting (add, list) be one. Say it's being included not as HTML but a separate entry point with view to use, e.g. /js/micro-frontend/commenting.js?view=list
    • Page edits history
    • ...

So that people could extend with any customElement framework they want, store in anything they want.

Other ideas could be we have a distribution of blogtini server soide to run on CloudFlare, or NGINX (NJS). Keeping the idea that the code run client-side or here would give the same (isomorphism FTW!). If one day a person stops using NGINX, it'll still work. Except that people who browse the web JavaScript disabled might get the stylized version.

The use of context-event for things allows to separate and defer later things. For example, not that it needs to be core, but I could decide to make more descriptive list of tags, with text in a language I maintain for the same tag definition. The logic is currently embedded in my 2020 pre-covid work in Vue, but the taxonomy logic, provide the appropriate data, and display the list differently

I gotta go. Told you, full of ideas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants