Bedrock 1.41 pre-release warning

Posted on 28 July 2021 at 10:34 by Wolfr_

This post is an explanation of what we are doing with #320 Hide core folder away from Bedrock users.

When this is done, you can upgrade Bedrock in the future simply by changing the version number and running npm install again:

"dependencies": {
    "@bedrock": "^1.41.0"
}

We will be deprecating the usage of bedrock-cli.

Breaking change incoming

Because of the above change, we need to make some breaking changes that Bedrock users will need to change manually.

Currently there is only one. If you use any relative paths to files in core in your content folder e.g. include ../../../../core/templates/mixins/icon-overview you need to change those to absolute paths include /core/templates/mixins/icon-overview .

We will provide a migration guide to move projects from pre-1.41 to 1.41.

Bedrock 1.39 pre-release warning: manual updates

Posted on 14 July 2021 at 13:27 by Wolfr_

Babel config

If you upgrade to Bedrock 1.39, you will need to add a file called babel.config.json to the root of your project with the following contents:

{
  "presets": [
    ["@babel/preset-env"]
  ],
  "plugins": [
    ["prismjs", {
        "languages": ["markup", "pug", "jsx"],
        "theme": "ghcolors"
    }]
  ]
}

Please refer to this file for the source.

Key for JSX code samples

Another manual upgrade is in the Bedrock config. Make sure you have an entry for styleguide.codeSamples.jsx (either true or false depending on preference). Please refer to this file for the source.

Updating Bedrock

As a reminder or a hint for new users, you can upgrade an existing Bedrock install by installing the CLI, then running

bedrock upgrade

For the dev version, use:

bedrock upgrade --dev

For the canary version, use:

bedrock upgrade --canary

Why manual file changes?

Some background: Bedrock-CLI only updates changes in the core folder (and a bit of wrangling in package.json).

Any other file changes outside have to be implemented manually.

I could theoretically update Bedrock-CLI to accord to this change, but not enough people are using Bedrock and/or upgrading Bedrock installs to justify coding this.

Bedrock’s branch structure

Posted on 8 July 2021 at 11:07 by Wolfr_

Just an informative post to make sense of Bedrock’s branch structure if you are interested in contributing.

Newest to stablest:

  • feature/x: branched off of development or canary depending on what it is
  • canary: runs in front of development
  • development: runs in front of master
  • master: official release

Bedrock 1.38.1 released

Posted on 8 July 2021 at 10:43 by Wolfr_

For the period of June 15 – July 8, we worked on several features in Bedrock 1.34 to Bedrock 1.38. Highlights include:

  • PurgeCSS support
  • PostCSS support
  • Different configs for development and production
  • Minification of CSS and JS for production builds
  • A way to customize the page tree so that markup is not “in the way” or even remove the page tree altogether (e.g. in production builds)

These were marked as pre-release until we were sure of moving along with all of it. We’ve let go of the pre-release label and these features are now part of the master branch.

I’ve now released Bedrock 1.38.1 which contains almost all of the recent features.

Some new things are in development like using Prism for code blocks and a “Copy as JSX” feature. These will likely land in a future release.

Devlog – converting this WordPress site to Bedrock itself

Posted on 4 July 2021 at 20:52 by Wolfr_

On one of those crazy programmer journeys

Yesterday I went on one of those crazy programmer journeys where I worked for 8-9 hours more or less non-stop. In the morning I had a discussion about all sorts of tecnical site-building-things with the crew over at Routify and this got me inspired to try some new things.

A better understanding of internals

I had been working on Bedrock for a while now, with a lof of work done in June, adding all kinds of stuff (PostCSS + PurgeCSS support for Tailwind purposes, a separate config for production etc.). This made me quite familiar with the Bedrock codebase.

(For those who don’t know what Bedrock is, it is a static site generator designed to help coding designers get up extensive HTML prototypes with hundreds of pages with surrounding styleguide and component document. We use it at Mono to prototype web apps at scale. It has been maintained as an MIT-licensed codebase for the past 6 years.)

However there were still a few parts of the code that were a bit of a mystery to me. There’s server.js which runs an Express server to get your templates on demand while working in Bedrock (avoiding static rebuilds, the way Jekyll used to work) and templates.js which builds the templates when you build a static site. Essentially they do the same, they generate the site, but one of them is at runtime based on a web request and another is when you do a static build. We will get back to this.

The site that you are reading this exact blog post on runs on WordPress (unless you are using a feed reader in which I salute you). I made this choice mostly because Bedrock never supported blogging in the first place.

The project: convert bedrockapp.org from WordPress to Bedrock itself

Now, because of the new features in Bedrock, I thought I might be able convert the Bedrock website to make it run using Bedrock. After all, since Bedrock is supposedly ready for production; why not show what we can do?

I also had reasons to dig into Express. I want to have more control for multi language setups. I want to know everything that is going on technically so I can fix problems if they arise.

So I set out in the afternoon to build new functionalities with the main focus being converting the whole website from a WordPress-driven website to one that runs on Bedrock.

For context… I am a coding designer

The initial code for Bedrock was written over 6 years ago by ex-colleague Thomas and over the years I’ve mostly maintained it, added new things but the core of it has mostly stayed the same. Thomas helped us to modernize a few things a couple of years ago too to make sure we were ready to use the next generation of JS inside of Bedrock.

I am not a programmer in my day-to-day job; but I am slowly but surely learning things. This blog post is mostly for my own reference and I doubt anyone will get through the whole thing. I like to write these kinds of things down because I know it will help me in the future.

Getting started

The first thing I did was use the this docs page’s instructions to get a recent version of Bedrock:

npx degit usebedrock/bedrock#canary bedrock-site-2

Then I looked into the WordPress repo for this website. I copied over images and SCSS and started converting the homepage. I used html2pug directly inside of my favorite editor TextMate using Filter through command to quickly convert HTML into Pug.

Filter Through Command
Screenshot from TM1 but the concept is the same in TM2. The command I am using for snippets of HTML is html2pug -f

I got the homepage up in seconds and everything went smooth. The SCSS pipeline was similar as the WordPress site and the homepage is more or less static.

On the homepage we have a dark hero area which doesn’t exist on the other pages. So there’s a bit of code I want to post here where we are checking if we are on the homepage or if we’re not.

if !pathname || pathname.match(/^index$/)
    img(src='/images/logo-white.svg')
else
    img(src='/images/logo-black.svg')
.u-sr-accessible Bedrock

Bedrock exposes the pathname on the template level which helps to make the page react to the current URL. I realize this variable is not that logically named. Above code can be handy; for selected states please refer to practical tips.

Rebuilding the documentation part

I then got to the documentation part. I figured I wanted to keep my documentation in markdown files. Currently the docs consist out of 15-ish pages. They are categorized in the sidebar but technically they are not categorized and really have the same structure:

/documentation/page-name

In Bedrock I created the markdown files in content/templates/documentation along with a documentation template, that would use marked to process the content:

extends /templates/_layouts/default

block content

    .c-container-outer
         .c-container-inner
                .c-cols
                     .c-col-4
                         include /templates/_includes/side-nav
                     .c-col-12
                            .c-content.c-sheet!=marked(doc.body)

Note how we are using != in Pug to render an unescaped string (see Interpolation).

Now, to make the connection between the pages I first set up that on a server level, if you would hit the /documentation/ part of the website with something behind that URL e.g. /documentation/my-page a different behavior would occur.

I did this in the aforementioned server.js:

  app.get('/documentation/:doc', function (req, res) {
    const docFilename = req.params.doc;
    const doc = _.find(contentDocs.discover().allContentDocs, doc => doc.attributes.filename === docFilename);
    renderView(req, res, 'documentation/_template/doc', {
      pathname: path.join('documentation/docs/', docFilename),
      doc
    });
  });

Essentially this is a customized copy of code that already existed for visiting a styleguide component. There’s a lot going on here.

I want to zoom in on the line that says:

const doc = _.find(contentDocs.discover().allContentDocs, doc => doc.attributes.filename === docFilename);

This line essentially uses a new file called content-docs.js (#) which exports a discover() function, which essentially looks in the right path (using this a path defined in paths.js) to find the aforementioned markdown files.

The code in content-docs.js processes the markdown files using a package called front-matter. Essentially we extract the content of the markdown file to a JS object along with anything contained in the front-matter. The resulting JS object looks a bit like this:

{
  body: "My documentation page",
  attributes: {
     title: "Title"
  }
}

This can then be used to build out the docs page. I’ll repeat the final line in the template here:

.c-content.c-sheet!=marked(doc.body)

Do you see how the doc const from above circles around to the template?

In another part of already shown code above we are referencing a template documentation/_template/doc . This is in a folder with an _ and will get ignored when doing a static build.

So the documentation worked, but only when running Bedrock using npm start (where the Express server is used).

What I had to do now was also make sure that the documentation part got generated statically when running npm run build.

In general, we are using Gulp as a JS task runner. When you start up Bedrock, Gulp is used to compile Javascript, preprocess your CSS and more. When you compile the static site, Gulp is also used to compile the templates. That’s exactly the functionality that I needed to modify.

In gulpfile.js I added a new gulp task:

gulp.task('templates:compile:contentDocs', templates.compile.contentDocs);

I also made sure this task ran as part of the regular build workflow.

Now in templates.js I used the same strategy I used before – carefully looked at the code that already exists, and customize the parts I needed. In this code we are using a function called getDefaultLocals to get in the content to render it. It takes the aforementione discover functions to return JS objects that we can use to render out the content.

function getDefaultLocals() {
  const defaultLocals = locals.getDefaultLocals();
  defaultLocals.docs = docs.discover();
  defaultLocals.contentDocs = contentDocs.discover();
  return defaultLocals;
}

I made a copy of the existing docs functionality (which is for rendering out docs in the styleguide, supporting Pug and Markdown templates) and made a new version called contentDocs:

contentDocs(done) {

  const defaultLocals = getDefaultLocals();

  const tasks = defaultLocals.contentDocs.allContentDocs.map(doc => {

    return gulp.src(paths.content.contentDocs.renderTemplate)

      .pipe(data(function (file) {
        return Object.assign({}, getDefaultLocals(), {
          doc,
          pathname: file.path.replace(path.join(process.cwd(), paths.content.templates.path), '').replace('.pug', ''),
        })
      }))
      .pipe(gulpPug(config.pug))
      .pipe(rename(function (path) {
        path.basename = doc.attributes.filename;
      }))
      .pipe(gulp.dest(paths.dist.contentDocs))


  })

  const stream = es.merge.apply(null, tasks);
  stream.on('end', done);
  return stream;

},

There is a lot going on here, but essentially we are doing the same as on the server: taking a template and applying it to content. The stream part of the code is to make it work with gulp.

Here, we are not doing it based on a single user request but we are processing a series of files. paths.content.contentDocs.renderTemplate refers to the aforementioned doc.pug ; using gulp-data we make sure the markdown content is available for the template to render.

So I got this part working:

  1. you could see a documentation page while developing using the server
  2. You could render out all documentation pages to static HTML

Rebuilding the blog part

I then tried to tackle the blog functionality. I got that working on the server first.

I used the WordPress Export functionality to grab an XML file with all my content in there. I then used a script to convert everything to files:

npx wordpress-export-to-markdown bedrock.WordPress.2021-07-03.xml

This gave me a back a series of .md files:

2015-12-22-welcome-to-bedrock.md
2015-12-23-a-quick-homepage-for-bedrock.md
2015-12-29-jade.md
2015-12-30-a-static-site-generator-history.md
2016-01-25-color-guide.md
2016-07-07-adding-selected-state-to-a-navigation-in-bedrock.md
...+53 more posts (59 blog posts in total)

I then set up things in similar ways as the documentation, thinking about the URLs:

app.get('/blog/:year/:month/:day/:title', function (req, res) {

  const blogFilename = req.params.year + '-' + req.params.month + '-' + req.params.day + '-' + req.params.title
  let post = _.find(blogPosts.discover().allBlogPosts, post => post.attributes.filename === blogFilename);

  renderView(req, res, 'blog/_template/post', {
    pathname: path.join('blog/', blogFilename),
    post
  });
});

This code so that people can visit /blog/:year/:month/:day/:title e.g. /blog/2021/07/04/my-post .

I would take the filename of the markdown file and process it from something like 2015-12-22-welcome-to-bedrock.md to 2015/12/22/welcome-to-bedrock using some simple replace logic:

parsedFile.url = filename.replace('.md','').replace('-','/').replace('-','/').replace('-','/')

I got the blog working quite quickly and I was pleased with myself.

For the overview I applied the .sort().reverse() methods so that the data would be from new to old.

For the index page I wrote some basic pagination logic.

-
    let perPage = 10;
    let lower = 0 + parseInt(page) * perPage;
    let higher = perPage + parseInt(page) * perPage;
    let posts = blogOverview.slice(lower,higher)

    // Pagination
    let hasNewerPage = false;
    let hasOlderPage = false;
    let olderPage;
    let newerPage;
    if (parseInt(page) === 1) {
        hasOlderPage = true;
        olderPage = parseInt(page)+1
    } else if (parseInt(page) > 1) {
        hasOlderPage = true;
        hasNewerPage = true;
        olderPage = parseInt(page)+1
        newerPage = parseInt(page)-1
    }

I really need to remember what slice() does because I always have to look it up.

Then depending on the existence of a newer or older page, show the pagination button:

.c-pagination

  if hasNewerPage
      a.c-btn.previous(href='/blog/'+newerPage) Newer
  if hasOlderPage
      a.c-btn.next(href='/blog/'+olderPage) Older

For clarity, on the blog index we are looping over blogOverview as follows:

each post in posts
   .c-article
       header.c-article-header
           h2
               a(href="/blog/"+post.url) #{post.attributes.title}
           p
               | Posted on #{post.attributes.date} 
               if post.attributes.time
                   | at #{post.attributes.time} 
               if post.attributes.author
                   | by #{post.attributes.author}

       .c-article-body.c-content!=marked(post.body)

We are using the aforementioned front matter attributes. This time with a bit more metadata:

---
title: "PurgeCSS support in Bedrock 1.34"
date: "2021-06-17"
author: Wolfr_
---

I could bother to make all the dates to be a standardized format but I didn’t. Somehow the export didn’t grab the post time but only the date. Since I sometimes write multiple posts in one day, this also creates an implementation problem. Ugh.

So all in all I was happy to have a blog working. You could go from the index page to the detail page and use the pagination to see all the posts. Awesome.

Then as with the documentation pages I realized I needed to make this work for the static build, else I could never finish the project. I spent some time trying to get this to work but I realized I needed:

  1. A format where the folders follow the generation logic (i.e. instead of a flat folder with a bunch of markdown files, folders for every year/month
  2. OR Generate said folders with a script on build time based on the contents of my flat folders

I don’t know enough about NodeJS and fs to comfortably do (2). I could do (1) and the aforementioned WordPress export plugin supports the generation of folders.

If I would ever make this a framework for other people to use I would force people to deal with a whole folder structure to make a new blog post, which is a pain.

Conclusions

So now I am in a bit of a pickle.

First of all, how to continue this work? It’s obviously not finished, because of the missing functionality to export the blog statically.

Should I push on? Shouldn’t I just keep this WordPress site running? I learned what I wanted to learn, does the world need another blogging engine that has less features than WordPress? I don’t like static blogging in practice anyway. I need a CMS and that is a whole problem case in itself.

Second, in Bedrock, does it really make sense to reimplement all functionality twice? First for the server use case and then for the static generation?

I guess it does for the core functionality of Bedrock.

The static site generation is awesome for CI purposes. We use this all the time at work. We will deploy prototypes and their different branches and all of it will be very easy with the setup that we have in place.

I suppose I could make a fork of Bedrock that is server-side only and that -has no build. But then I would also encounter my skill ceiling.

I have no knowledge setting up Express servers to deploy on the internet. I suppose it is a new skill I could learn. Heroku is the first thing that comes to mind. Then maybe I could also experiment with posting forms to the server and saving things to a database.

Oh, all the things I could learn!

For now I am just happy to have learned more about Bedrock’s internals. You know, for if I ever need to fix something to the actual scope of the project.

If you are curious, I put the site up at https://github.com/usebedrock/bedrock-site-2 . If you want to hack on it, happy coding. If you’re interested in Bedrock in general, give it a try and if you are stuck, open an issue.

Oh and if you made it to the end, please let me know on Twitter.

Bedrock: now ready for production*

Posted on 22 June 2021 at 16:54 by Wolfr_

It used to be the case that we assumed that if you used Bedrock, you would make a prototype, and it would require a few changes to bring it to production.

For example, a typical process would be that we’d make an HTML/CSS prototype and a JS or PHP developer would take our templates and then extract these to the real app context.

We would write HTML and CSS, document some components, but leave the code non-minified.

If you’d look at most Bedrock installs closely you’d also see a specific div structure to make the page tree feature work.

If you’d look at the JS output you’d also see a relatively large bundle that is meant to make the styleguide work.

Lately I was thinking about why we have this distinction: why can’t Bedrock be the tool to also ship the production project?

We already had a build pipeline, we already had a config file. With some additions I thought we could make it work.

If you’ve been following along for the past few days you might’ve seen a lot of activity on the Bedrock repository.

Spurred by 2 recent projects that each came with their own requirements, the stars aligned and I got really deep into extending Bedrock. The result: it is now possible to use Bedrock to build sites for production.

The default output of the first page of Bedrock now looks like this:

<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bedrock</title>
    <link rel="stylesheet" href="/css/main.css">
</head>
<body>
    <p>Welcome to Bedrock. This is your homepage. Go to <code>/content/templates/index.pug</code> to find this file and start editing your project.</p>
    <script src="/js/bundle-client.js"></script>
</body>
</html>

That’s it. That’s the code. Now, with this output, there is no styleguide and page tree feature. If you want to enable those, you can do so in the configuration. There are now separate config files for development and production.

To get the above output, the config flags you need (in bedrock.config.prod.js) are:

/**
 * Bedrock production configuration
 * ---
 * This object will get merged into the bedrock config object and contains specific values for production
 * Use `NODE_ENV=production npm run build` to run a production build
*/

module.exports = {
  noIndex: false,
  pageTree: false,
  styleguide: false,
  js: {
    minify: true
  },
  css: {
    minify: true,
    purge: true,
    compiler: 'scss'
  },
};

This will give you minimal, compressed output it the build.

In this setup we never load the big JS file to make the prototype work (the one that contains jQuery (legacy code see #367), Codemirror for syntax highlighting etc.).

To reflect this new logic, I updated the from prototype to production page.

If you have any questions about Bedrock, don’t be scared to make an issue, comment to a blog post or ask a question through e-mail.

*This feature is still in a pre-release phase, once it’s stable we will release it to Bedrock’s master branch.

How the single-command install of Bedrock works

Posted on 22 June 2021 at 10:09 by Wolfr_

How did we come up with a single-command install for Bedrock and how does it work? The logic relies on two things: degit and npx.

The degit command allows you to clone a repository without the Bedrock Git repository itself (since you won’t be working on Bedrock, but on your own project). Degit was created by Rich Harris of Svelte fame.

Npx is a node feature since Node 5.2. It’s a package runner that allows you to run a allows you to run a command without having to install it.

The combination of these is what allows you to install Bedrock with a single command, provided you have a recent version of Node installed.

Setting a class to the html or body tag in Bedrock

Posted on 22 June 2021 at 10:01 by Wolfr_

Bedrock provides two default variables: htmlClass and bodyClass. When you want to set an HTML class for every page, simply go to _layouts/master.pug and find these lines:

block pageVariables
    //- Use this block to append page variables in templates
    - var projectTitle = "Bedrock"
    - var htmlClass = ""
    - var bodyClass = ""

Now you can change the empty string to something that you like.

If you want to only add a certain body class in a specific template, use Pug’s template inheritance to append to the block. For example, let’s say you already have a utility class to set full height on the pageVariables block in master.pug, but you want to set a specific body class on another page, it will look like this.

In master.pug:

block append pageVariables
    - var htmlClass = "u-full-height"
    - var bodyClass = "u-full-height"

in your template (do repeat the original class value):

block append pageVariables
    - var bodyClass = "u-full-height your-specific-class"

You can use this technique to set global variables or page-specific variables; or even page-specific variables that inherit to deeper templates. You could probably be more clever than this code and append to the original value too. Anyone knows how?

How to add scripts to header and footer

Posted on 22 June 2021 at 9:54 by Wolfr_

For head-positioned JS, use the default headerScripts block in a template:

extends /templates/_layouts/master

block append headerScripts
    script(src='/js/my-file.js')


block body
    // My content

For footer-positioned JS, use the default footerScripts block:

extends /templates/_layouts/master

block append footerScripts
    script(src='/js/my-file.js')

block body
    // My content

Current Bedrock logic

Posted on 21 June 2021 at 18:54 by Wolfr_

Updated June 22

Just a note for those interested – there’s a lot of activity on the repos, here is the current version logic:

  • Stable: 1.33.0
  • Development: 1.34.0
    • PurgeCSS feature
    • PostCSS
  • Canary: 1.38
    • Minification of JS and CSS feature
    • Development and production builds

As we use the Canary and Development versions in real projects and bugs get fixed, we will move them to a stable release.

Do note that we put detailed release notes on Github.