Configuration

These are all the supported configuration options in Greenwood, which you can define in a greenwood.config.js file in your project's root directory.

The below is a greenwood.config.js file reflecting default values:

export default {
  devServer: {
    extensions: [],
    hud: true,
    port: 1984,
    host: 'localhost'
  },
  basePath: '',
  port: 8080,
  interpolateFrontmatter: false,
  markdown: {
    plugins: [],
    settings: {}
  },
  prerender: false,
  staticRouter: false,
  optimization: 'default',
  plugins: [],
  workspace: new URL('./src/', import.meta.url),
  pagesDirectory: 'pages', // e.g. src/pages
  layoutsDirectory: 'layouts', // e.g. src/layouts
  isolation: false,
  polyfills: {
    importAttributes: null, // e.g. ['css', 'json']
    importMaps: false
  }
};

Base Path

There are cases where an application might be deployed and hosted from a "sub" pathname that acts as the relative "web root". (GitHub Pages is an example of this)

So with a URL of http://www.example.com/app-a/, the basePath could be set as such:

export default {
  basePath: '/app-a'
};

This would then configure Greenwood's routing and <script> and <link> tags to reference this segment automatically. For example:

<script type="module" src="/app-a/some-script.a243dccss.js"></script>

For convenience, the value of basePath will also be made available as a global variable in the <head> of your pages. For example:

<script data-gwd="base-path">
  globalThis.__GWD_BASE_PATH__ = '/app-a';
</script>

User content, like <a> and <img> tags will require manually prefixing the basePath in your code.

Dev Server

Configuration for Greenwood's development server is available using the devServer option.

  • extensions: Provide an array of extensions to watch for changes and reload the live server with. By default, Greenwood will already watch all "standard" web assets (HTML, CSS, JS, etc) it supports by default, as well as any extensions set by resource plugins you are using in your greenwood.config.json.
  • hud: The HUD option (head-up display) is some additional HTML added to your site's page when Greenwood wants to help provide information to you in the browser. For example, if your HTML is detected as malformed, which could break the parser. Set this to false if you would like to turn it off.
  • port: Pick a different port when starting the dev server
  • proxy: A set of paths to match and re-route to other hosts. Highest specificity should go at the end.

Example

export default {
  devServer: {
    extensions: ['txt', 'rtf'],
    port: 3000,
    proxy: {
      '/api': 'https://stage.myapp.com',
      '/api/foo': 'https://foo.otherdomain.net'
    }
  }
};

Interpolate Frontmatter

To support simple static templating in HTML and markdown pages and layouts, the interpolateFrontmatter option can be set to true to allow the following kinds of simple static substitutions using a syntax convention based on JavaScript template literals.

Example

Given some frontmatter in a markdown file:

---
layout: post
title: Git Explorer
published: 04.07.2020
description: Local git repository viewer
author: Owen Buckley
image: /assets/blog-post-images/git.png
---

It can be accessed and substituted statically in either markdown or HTML.

Markdown
# My Blog Post

Published: ${globalThis.page.published}

Lorum Ipsum.
HTML
<html>
  <head>
    <title>My Blog - Configuration</title>
    <meta name="author" content="${globalThis.page.author}">
    <meta property="og:title" content="My Blog">
    <meta property="og:type" content="website">
    <meta property="og:url" content="https://www.myblog.dev">
    <meta property="og:image" content="https://www.myblog.dev/${globalThis.page.image}">
    <meta property="og:description" content="My Blog - ${globalThis.page.description}">
  </head>
  <body>
    ...
  </body>
</html>

Isolation Mode

If running Greenwood as a server in production with the greenwood serve command, it may be desirable to isolate the server rendering of SSR pages and API routes from the global runtime (e.g. NodeJS) process. This is a common assumption for many Web Component libraries that may aim to more faithfully honor the browser's native specification on the server.

Examples include:

  • Custom Elements Registry - Per the spec, a custom element can only be defined once using customElements.define.
  • DOM Shims - These often assume a globally unique runtime, and so issues can arise when these DOM globals are repeatedly loaded and initialized into the global space

See these discussions for more information

As servers have to support multiple clients (as opposed to a browser tab only serving one client at a time), Greenwood offers an isolation mode that can be used to run SSR pages and API routes in their own context per request.

Example

To configure an entire project for this, simply set the flag in your greenwood.config.js

export default {
  isolation: true // default value is false
};

Optionally, you can opt-in on a per SSR page / API route basis by exporting an isolation option.

// src/pages/products.js

export const isolation = true;

Markdown

You can install and provide custom unifiedjs presets and plugins to further customize and process your markdown past what Greenwood does by default. After running an npm install you can provide their package names to Greenwood.

Example

export default {
  markdown: {
    settings: { commonmark: true },
    plugins: [
      'rehype-slug',
      'rehype-autolink-headings'
    ]
  }
};

Optimization

Greenwood provides a number of different ways to send hints to Greenwood as to how JavaScript and CSS tags in your HTML should get loaded by the browser. Greenwood supplements, and builds up on top of existing resource "hints" like preload and prefetch.

OptionDescriptionUse Cases
defaultWill add a <link rel="..." src="..." as="..."></link> tag for every <script> or <link> tag in the <head> of your HTML using preload for styles and modulepreload for scripts. This setting will also minify all your JS and CSS files.General purpose.
inlineUsing this setting, all your <script> and <link> tags will get inlined right into your HTML.For sites with smaller payloads, this could work best as with inlining, you do so at the expense of long-term caching.
noneWith this setting, none of your JS or CSS will be minified or hinted at all.The best choice if you want to handle everything yourself through custom Resource plugins.
staticOnly for <script> tags, but this setting will remove <script> tags from your HTML.If your Web Components only need a single render just to emit some static HTML, or are otherwise not dynamic or needed at runtime, this will really speed up your site's performance by dropping unnecessary HTTP requests.

These settings are currently considered experimental. Additional improvements and considerations include adding none override support, SSR + hydration, and side effect free layouts and pages.

Example

export default {
  optimization: 'inline'
};

Overrides

Additionally, you can apply overrides on a per <link> or <script> tag basis by adding a custom data-gwd-opt attribute to your HTML. The following is supported for JavaScript and CSS.

<!-- Javascript -->
<script type="module" src="/path/to/file1.js" data-gwd-opt="static"></script>
<script type="module" src="/path/to/file2.js" data-gwd-opt="inline"></script>

<!-- CSS -->
<link rel="stylesheet" href="/path/to/file1.css" data-gwd-opt="inline"/>

Just be mindful that style encapsulation provided by ShadowDOM (e.g. :host) for custom elements will now have their styles inlined in the <head> and mixed with all other global styles, and thus may collide and be susceptible to the cascade depending on their degree of specificity. Increasing specificity of selectors or using only global styles will help resolve this.

Pages Directory

By default the directory Greenwood will use to look for your local content is pages/. It is relative to your user workspace setting. (${userWorkspace}/${pagesDirectory})

Example

export default {
  pagesDirectory: 'docs' // Greenwood will look for pages at src/docs/
};

Polyfills

Greenwood provides polyfills for a few Web APIs out of the box.

Import Maps

Only applies to development mode.

If you are developing with Greenwood in a browser that doesn't support import maps, with this flag enabled, Greenwood will add the ES Module Shims polyfill to provide support for import maps.

export default {
  polyfills: {
    importMaps: true
  }
};

Import Attributes

Import Attributes, which are the underlying mechanism for supporting CSS and JSON module scripts, are not widely supported in all browsers yet. Greenwood can enable this in a browser compatible why by specifying which attributes you want handled. In both cases, Greenwood bundles these as ES Modules and will strip the attributes syntax.

export default {
  polyfills: {
    importAttributes: ['css', 'json']
  }
};

In the case of CSS, Greenwood will inline and export your CSS as a Constructable Stylesheet

// this
import sheet from './styles.css' with { type: 'css'};

// will fallback to this
const sheet = new CSSStyleSheet();sheet.replaceSync(' /* ... */ ');export default sheet;

For JSON, Greenwood will simply export an object

// this
import data from './data.css' with { type: 'json'};

// will fallback to this
export default { /* ... */ }

Port

Unlike the port option for devServer configuration, this option allows you to configure the port that your production server will run on when running greenwood serve.

Example

export default {
  port: 8181
};

Prerender

When set to true Greenwood will pre-render your application using WCC and generate HTML from any Web Components you include in your pages and layouts as part of the final static HTML build output.

You can combine this with "static" components so that you can just do single pass rendering of your Web Components and get their output as static HTML and CSS at build time without having to ship any runtime JavaScript!

Example

export default {
  prerender: true
};

Static Router

⚠️ This feature is experimental. Please follow along with our discussion to learn more.

Setting the staticRouter option to true will add a small router runtime in production for static pages to prevent needing full page reloads when navigation between pages that share a layout. For example, the Greenwood website is entirely static, outputting an HTML file per page however, if you navigate from the Docs page to the Getting Started page, you will notice the site does not require a full page load. Instead, the router will just swap out the content of the page much like client-side SPA router would. This technique is similar to how projects like pjax and Turbolinks work, and like what you can see on websites like GitHub.

Example

export default {
  staticRouter: true
};

Layouts Directory

By default the directory Greenwood will use to look for your layouts is in layouts/. It is relative to your user workspace setting. (${userWorkspace}/${layoutsDirectory})

Example

export default {
  layoutsDirectory: 'layouts' // Greenwood will look for layouts at src/layouts/
};

Workspace

Path to where all your project files will be located. Using an absolute path is recommended. Default is src/.

Example

Setting the workspace path to be the www/ folder in the current directory from where Greenwood is being run.

export default {
  workspace: new URL('./www/', import.meta.url)
};

Please note the trailing / here as for ESM, as paths must end in a / for directories.