Skip to content

Commit

Permalink
Merge pull request #121 from thedodd/blog
Browse files Browse the repository at this point in the history
Add blog section to the site & add first blog post
  • Loading branch information
thedodd committed Feb 6, 2021
2 parents 9031177 + 958a802 commit 1a497f5
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<div align="center">

[![Build Status](https://github.com/thedodd/trunk/workflows/ci/badge.svg?branch=master)](https://github.com/thedodd/trunk/actions)
[![](https://img.shields.io/crates/v/trunk.svg?style=flat-square)](https://crates.io/crates/trunk)
[![](https://img.shields.io/crates/v/trunk.svg?color=brightgreen&style=flat-square)](https://crates.io/crates/trunk)
[![](https://img.shields.io/homebrew/v/trunk?color=brightgreen&style=flat-square)](https://formulae.brew.sh/formula/trunk)
![](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=flat-square)
[![Discord Chat](https://img.shields.io/discord/793890238267260958?logo=discord&style=flat-square)](https://discord.gg/JEPdBujTDr)
![](https://img.shields.io/crates/d/trunk?label=downloads%20%28crates.io%29&style=flat-square)
Expand Down
1 change: 1 addition & 0 deletions site/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ favicon = "rustacean-flat-happy.svg"
juice_logo_name = "Trunk"
juice_logo_path = "rustacean-flat-happy.svg"
juice_extra_menu = [
{ title = "Blog", link = "/blog/"},
{ title = "Github", link = "https://github.com/thedodd/trunk"}
]
49 changes: 49 additions & 0 deletions site/content/blog/2021-02-03-release-0_8_0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
+++
title = "Trunk v0.8.0 & Project Update"
description = "A new website, new maintainers, and a new Trunk release."
[extra]
author = "Doddzilla"
+++

Hello fellow Rustaceans! Welcome to the very first official blog post of the Trunk community. In case you don't know, Trunk is a WASM web application bundler for Rust. It works out of the box with any web UI framework based on `wasm-bindgen`, and supports bundling of a good number of asset types including SASS/SCSS, CSS, JS snippets, images, and more. There's a lot to cover here, so let's dig in.

# New Site & New Blog
First off, if you are reading this blog post, then you've found our new Trunk website. All of our documentation is here, and the maintainers team plans on continuing to expand this site, adding new documentation and improving the site as we go. This is a community driven website, so if you see things which you would like to improve, content you would like to add (including new blog posts), or any other sorts of changes, please open a PR! Just to make that process even easier, let's talk about how the site is built.

We are using the excellent [Zola static site engine](https://getzola.org). It is another great Rust project. Putting together this entire site from nothing took only a few hours. Sure there's not too much here, but the quality is quite solid, and Zola really delivered on providing a robust development process. Using Zola to make changes to this site will be quite simple for newcomers and experienced users alike.

We are also using the Zola [Juice theme](https://juice.huhu.io/). I have to say, I quite like the theme, and very few changes were needed to make things work.

For now we are using Github Pages to host the site. This was so quick and easy, there are Github Actions which do most of the heavy lifting. Hard to beat that level of convenience.

Overall, adding a new blog post for maintainers and community members will typically be as simple as adding a new markdown file with your content ... AND THAT'S ALL. Doesn't get easier than that. It is the hope of the maintainers team that community members will feel empowered to write a blog post on anything and everything related to Trunk. Use cases, future plans for contributions, pain points, areas where they would like to see Trunk improve, how to use Trunk with [Seed](https://github.com/seed-rs/seed), [Yew](https://github.com/yewstack/yew), or any other project. The more content the better.

Last thing I will say about the site, we are in the process of adding some new art work. Right now we've got a placeholder Ferris holding down the fort. They're doing a great job, but we're looking for some Trunk specific art, images, Ferris logos, etc. We've got some discussion going on over in [Trunk #120](https://github.com/thedodd/trunk/issues/120). I am especially excited about the possibility of an SVG of a bunch of little Ferris crabs packing a trunk full of sea shells, assets logos, scripts/scrolls, coral chunks and the like. If you've got some skills on this front, and you would like to jump on this hype train, DO IT!!! **We would love to show off your skills on the site.**

# New Maintainers
The biggest news we have, and probably one of the best things for the Trunk project itself, is that we have 3 new maintainers!!! 🎉🍻 Rakshith Ravi, Philip Peterson and Martin Kavik. I'm very excited to have these guys on the team, they've each already contributed in many ways to the Trunk project, and having a strong group of maintainers is very important for the long-term success of any open source project. We are all stronger together!

If you are interested in helping out as a maintainer, the best way to get started is with PR reviews, reviewing issues and making sure they are ready to be moved to the coding phase, and then actually getting your hands dirty with some code! Trunk so far has been able to maintain a pretty solid and enjoyable code base. Now is a great time to get involved.

# Trunk v0.8.0 has Landed
Trunk v0.8.0 has now been released. See the [release page](https://github.com/thedodd/trunk/releases/tag/v0.8.0) for all of the details. In summary:

- The `trunk watch` & `trunk serve` commands now accept the `--watch` option which allows users to specify multiple different directories and files to be watched, triggering new builds as changes are detected. A community member `@malobre` knocked this one out.
- Another community member `@DzenanJupic` has added the `inline` asset type. This allows users to target various file types and have their content "inlined" into the parent HTML document as-is. Go checkout the docs on the [inline asset type](https://trunkrs.dev/assets/#inline) for more details (wow, felt great to be able to provide a link to the docs like that).
- A community member `@hamza1311` & one of our maintainers `@philip-peterson` squashed a bug where old build artifacts would continually accumulate in the dist dir. That has now been fixed, and any time a successful build goes through, all of the old artifacts will be removed, and only the fresh new artifacts will exist in the `dist` dir.
- Lastly, `@MartinKavik` fixed an infinite rebuild loop bug on Windows.

# Immediate Plans
In the immediate term, we have a few community lead efforts for cutting Trunk over to the Tokio ecosystem. This is primarily to address our need for a robust WebSockets & SSE solution, and the Tokio community happens to have some pretty solid options on that front.

We've also got another community lead effort on getting `wasm-opt` integrated into the standard Trunk build pipeline.

Both of these initiatives are exciting and will go far to provide an even better experience for folks building Rust WASM applications using Trunk.

# Conclusion
I am quite excited and optimistic about Rust's future with WebAssembly. WebAssembly has a long way to go, but it has already come so far. Working together to continue making Rust the best option for WebAssembly, and to ship more safe, robust, and powerful Rust code to the web is definitely for the better as far as I can see.

**BONUS:** head on over to [the next blog post](/blog/plugin-system/) where we discuss design patterns around a Trunk plugin system.

Let's keep on doing this. Cheers!

89 changes: 89 additions & 0 deletions site/content/blog/2021-02-05-plugin-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
+++
title = "Design Thoughts — Trunk Plugin System"
description = "Thoughts and considerations on various design patterns for a Trunk plugin system."
[extra]
author = "Doddzilla"
+++

Hello again! For this post, I would like to share my thoughts on a few possible design patterns for a Trunk plugin system. We will review a subset of the possible patterns based on some community discussion so far, looking at pros, cons, limitations and other things to consider along the way.

# Why Plugins?
The Trunk community has been discussing plans to introduce a plugin system for Trunk. A good question to ask is: why? There are definitely a few good reasons:

- There is a lot more work to be done on Trunk, and plugins would allow for folks to quickly and easily integrate with the Trunk build system in ways which are not yet supported.
- Proprietary integration. Perhaps folks have some Trunk pipelines they would like to build, but they are not able to open source the code. This would provide for such cases.
- Perhaps folks don't want to take the time to contribute 😬. The maintainers are too mean, too snarky, they always ask me to change things ... who knows?
- Perhaps the use case is too niche, and the Trunk maintainers don't think it should be in the code base.
- A big one is maintenance. As Trunk grows and new use cases inevitibly emerge, that will be more and more code to maintain, which may slow overall progress of core Trunk development.

# Trunk Internals (abridged)
To have a meaningful discussion on possible Trunk plugin designs, let's take a quick look at the internals of Trunk. There are 3 primary layers:

- **Configuration:** this includes `Trunk.toml`, env vars, & CLI args/opts. These are merged based on a cascade and then fed into the lower layers.
- **CLI Commands:** this is what tells Trunk what to do.
- **Pipelines:** all of the goodness of Trunk comes from the various pipelines we've created in this layer. This is where we process HTML, SASS/SCSS, CSS, images, JS, etc.

## Pipelines
The entire build system is a composition of various asynchronous pipelines. The very first pipeline is the HTML pipeline. This directly corresponds to the source HTML file (typically the `index.html`), and this pipeline is responsible for spawning various other pipelines based on the directives found in the source HTML. As a reminder, Trunk uses the `<link data-trunk rel="{{TYPE}}" ..args.. />` pattern in the source HTML to declare asset pipelines which Trunk should process.

Each pipeline is given all of the data found in its corresponding `<link>`, along with some additional general data, like config and such. This is then spawned off as an async task. Currently there are no dependencies between pipeliens, however the HTML pipeline will not complete until all of its spawned pipelines have finished.

Ultimately, each pipeline can do whatever it needs to do, producing various artifacts and writing them to the Trunk staging directory. Once a pipeline finishes, it will return a manifest of data along with a callback. That callback is called by Trunk providing a mutable reference to the will-be final HTML, and that callback can update the HTML in whatever way it needs, which is typically just to remove its original `link`, and maybe append some new data which points to newly generated artifacts. Once all of the pipelines have completed, Trunk will perform a few final tasks to provide a nice pristine `dist` dir ready to be served to the web.

Simply speaking, it could be said that Trunk is really just a system for running various sorts of pipelines/tasks which operate on an HTML document.

# Embedded WASM Runtime
One pattern which is rather exciting would be to use an embedded WASM runtime like [https://wasmer.io/](https://wasmer.io/) or [https://wasmtime.dev/](https://wasmtime.dev/) as the basis for our plugin system.

We could define a WASM import/export interface where a WASM module may be loaded by Trunk, we query its Trunk ABI version, and then we call that module's `main` function (or whatever we decide to call it) and pass it a compatible data structure corresponding to the data which normal Trunk pipelines would receive.

Those WASM plugin functions could return compatible data structures and manifests just like normal pipelines, and then we call another function on that WASM module, say `callback`, providing it HTML and then expecting HTML in response. This would be pretty damn close to the current pipeline system.

## Plugin Discovery
Plugins could be declared in the `Trunk.toml`. A section of the TOML, say `[plugins]`, would allow users to declare a module by name, along with some info on how to download the corresponding WASM. Trunk would fetch and cache these modules, and then modules could be selected for use in the source HTML via a `<link data-trunk rel="plugin" data-plugin="{{NAME}}" .../>` or the like. This would give Trunk a fairly robust mechanism for detecting that a directive should use a plugin, and we would also know the name of the plugin as provided by the `data-plugin` attr.

I've considered perhaps leveraging [https://wapm.io/](https://wapm.io/) for this. Folks could use the WAPM registry for their Trunk plugins. Maybe we just allow folks to specify a URL (like the URL of an asset from a Github release) where Trunk can download the WASM module. Perhaps we support multiple patterns.

## Capability Attrs
When a plugin is declared in the source HTML, attributes could be provided in the corresponding link which declare the "capabilities" which should be given to the plugin. Something like `data-plugin-capabilities="capX,capY"`, or perhaps something more granular. Various capabilities could be listed, giving the Trunk user full control over what the plugin can and can not do.

Having used many JS bundlers and build tools, many of which have their own plugin systems, I have very often been quite paranoid about not knowing EXACTLY what those plugins are doing. Lots of fun exploits out there. This seems like a great way to help mitigate such issues. Plugins could declare the capabilities they need, and Trunk can abort a pipeline early if the user has not provided the needed capabilities, helping to ensure users don't run into unexpected issues.

This seems quite nice. There are lots of additional features which could be developed along these lines. New Trunk capabilities could be exposed over time as we develop new features. I dig it.

## Trunk Plugin Lib
Along with this approach we would release a `trunk-plugin` crate which does everything for you. The only thing plugin authors would need to do is write the actual business logic of their plugin. Some of the things it would do:

- it would have cargo features for opting into the various capabilities which Trunk can expose to plugins, and would expose code to actually utilize those capabilities from within the plugin.
- opting into various capabilities would automatically update an exported WASM function to tell Trunk which capabilities the plugin needs.
- all of the WIT business would be taken care of by this crate. Authors would not have to worry about how to move data across the WASM boundary.

## Pros
Ok, so let's recap the goodness.

- WASM gives us a universal runtime, so plugins could be written in any compatible WASM language (Rust, C, C++, Swift, Go, AssemblyScript, &c).
- Plugins would only ever need to be compiled for one architecture: WASM.
- Capability-based security. If a plugin needs network or filesystem access, this might be a good time to go review the code in the plugin.
- The Trunk maintainers would publish a crate to handle all of the heavy lifting related to creating plugins.

## Cons
- Building capabilities will take time and deeper design work, though this will be true for any plugin system.
- WASM does not yet support async paradigms, so basically all plugins will need to be spawned onto an async threadpool. Not really a big deal.
- We would be moving somewhat out of the happy and safe Rustacean tide pool and into the cold scary world of WASM, other package managers (potentially), and an unstable WASM/WASI/WIT set of specs ... though we are already a Rust WASM web bundler. Additionally, we would quite likely not use WASI at all, and instead expose our own set of capabilities.

# Dynamic Linking (via Stable ABI)
We've looked into using [abi_stable_crates](https://github.com/rodrimati1992/abi_stable_crates/). This would be Rust dynamic linking via a stable subset of the Rust ABI (provided by this crate, because Rust itself does not have a stable ABI), and then leveraging this crate's runtime logic to verify the safety of dynamically linking to the various plugins.

We would need our own discovery system for this, and plugins would need to be built by Trunk (most likely), as they would need to be built for the host tripple. There would be a fair bit of overhead with this approach. It would not be as rigid as C FFI, and it would still be safe Rust<->Rust communication, but we would need to use a limited subset of types at the boundary.

# Just Don't
We could just not do a plugin system. There are already a few nice improvements we can make to the current process of adding new pipeline types. By my analysis, we should switch over to a dynamic dispatch model for the response data from pipelines. I only say this because the current approach uses enums (which I love), but folks unfamiliar with the code base may see this as tedious to update.

The process of adding new pipeline types is already pretty simple, we can make it even more simple. Perhaps this topic merits a blog post of its own.

# Conclusion
All in all, I'm pretty stoked about the possibility of using a WASM runtime for Trunk plugins. The security improvements (over other plugin models) are real, the simplicity of distributing ready to execute WASM is real, I dig it.

There is some good discussion over in [Trunk #104](https://github.com/thedodd/trunk/issues/104), and more discussion to be had. This blog post should be seen as part of that discussion. If you have thoughts or ideas on this topic, please drop by and share. We would love to hear from you!

Cheers!
12 changes: 12 additions & 0 deletions site/content/blog/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
+++
title = "Blog"
sort_by = "date"
template = "blog.html"
page_template = "blog-post.html"
+++

# Blog

Welcome to the Trunk blog!

If you are interested in creating content for the blog, please reach out to anyone on the Trunk maintainers team, we will be happy to work with you on creating some content.
10 changes: 10 additions & 0 deletions site/sass/blog-post.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.trunk-blog-author {
margin: 0 0 2em 0;
padding: 0;
font-size: 0.8em;

p {
margin: 0;
padding: 0;
}
}
34 changes: 34 additions & 0 deletions site/templates/blog-post.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{% import "juice/templates/_macros.html" as macros %}
{% extends "index.html" %}

{% block title %}{{ page.title }} | {{ super() }} {% endblock title %}

{% block head %}
{{ super() }}
<link rel="stylesheet" type="text/css" href="/blog-post.css">
{% endblock head %}

{% block header %}
<header class="box-shadow">
{{ macros::render_header() }}
</header>
{% endblock header %}

{% block content %}
<div class="trunk-blog-author">
{% if page.extra.author %}
<p>Author: {{page.extra.author}}</p>
{% endif %}
{% if page.date %}
<p>Date: {{page.date}}</p>
{% endif %}
</div>

<h1>{{ page.title }}</h1>

{% if page.description %}
<p class="subtext">{{ page.description }}</p>
{% endif %}

{{ page.content | safe }}
{% endblock content %}
33 changes: 33 additions & 0 deletions site/templates/blog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% import "juice/templates/_macros.html" as macros %}
{% extends "index.html" %}

{% block title %}{{ section.title }} | {{ super() }} {% endblock title %}

{% block header %}
<header class="box-shadow">
{{ macros::render_header() }}
</header>
{% endblock header %}

{% block toc %}
<div class="toc">
<div class="toc-sticky">
{% for post in section.pages %}
<div class="toc-item">
<a class="subtext" href="{{post.permalink | safe}}">{{ post.title }}</a>
</div>
{% endfor %}
</div>
</div>
{% endblock toc %}

{% block content %}
<div class="heading-text">{{ section.description }}</div>
{{ section.content | safe }}

{% for post in section.pages %}
<h1><a href="{{ post.permalink }}">{{ post.title }}</a></h1>
<p class="subtext">{{ post.description }}</p>
{% endfor %}

{% endblock content %}
Loading

0 comments on commit 1a497f5

Please sign in to comment.