• Stars
    star
    193
  • Rank 201,081 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Prerender Ember apps with Fastboot at build time.

prember = Pre Render Ember

A progressive static-site generator for Ember

This Ember addon allows you to pre-render any list of URLs into static HTML files at build time. It has no opinions about how you generate the list of URLs.

Features

  • πŸ’―100% Ember
  • πŸš€ Blazing optimized for speed.
  • 🚚 Data Agnostic. Supply your site with data from anywhere, however you want!
  • πŸ’₯ Instant navigation and page views
  • β˜”οΈ Progressively Enhanced and mobile-ready
  • 🎯 SEO Friendly.
  • πŸ₯‡ Ember-centric developer experience.
  • 😌 Painless project setup & migration.
  • πŸ“¦ Embroider support

Quick Start

Add these packages to your app:

ember install ember-cli-fastboot
ember install prember

And configure some URLs that you would like to prerender:

// In ember-cli-build.js
let app = new EmberApp(defaults, {
  prember: {
    urls: [
      '/',
      '/about',
      '/contact'
    ]
  }
});

When you do ember build --environment=production, your built app will include fastboot-rendered HTML in the following files:

/index.html
/about/index.html
/contact/index.html

Explanation

When you build a normal ember app (ember build --environment=production) you get a structure something like this:

dist/
β”œβ”€β”€ assets
β”‚Β Β  β”œβ”€β”€ my-app-0d31988c08747007cb982909a0b2c9db.css
β”‚Β Β  β”œβ”€β”€ my-app-bdaaa766a1077911a7dae138cbd9e39d.js
β”‚Β Β  β”œβ”€β”€ vendor-553c722f80bed2ea90c42b2c6a54238a.js
β”‚Β Β  └── vendor-9eda64f0de2569c64ba0d33f08940fbf.css
β”œβ”€β”€ index.html
└── robots.txt

To serve this app to users, you just need to configure a webserver to use index.html in response to all URLs that don't otherwise map to files (because the Ember app will boot and take care of the routing).

If you add ember-cli-fastboot to your app, it augments your build with a few things that are needed to run the app within node via fastboot:

dist/
β”œβ”€β”€ assets
β”‚Β Β  β”œβ”€β”€ assetMap.json
β”‚Β Β  β”œβ”€β”€ my-app-0d31988c08747007cb982909a0b2c9db.css
β”‚Β Β  β”œβ”€β”€ my-app-a72732b0d2468246920fa5401610caf4.js
β”‚Β Β  β”œβ”€β”€ my-app-fastboot-af717865dadf95003aaf6903aefcd125.js
β”‚Β Β  β”œβ”€β”€ vendor-553c722f80bed2ea90c42b2c6a54238a.js
β”‚Β Β  └── vendor-9eda64f0de2569c64ba0d33f08940fbf.css
β”œβ”€β”€ index.html
β”œβ”€β”€ package.json
└── robots.txt

You can still serve the resulting app in the normal way, but to get the benefits of server-side rendering you would probably serve it from a fastboot server that knows how to combine the JS files and the index.html file and generate unique output per URL. The downside of this is that your fastboot server is now in the critical path, which increases your ops complexity and is necessarily slower than serving static files.

prember starts with an app that's already capable of running in fastboot and augments it further. You configure it with a source of URLs to prerender, and it uses Fastboot to visit each one during the build process, saving the resulting HTML files:

dist/
β”œβ”€β”€ _empty.html            <--------- A copy of the original index.html
β”œβ”€β”€ about
β”‚Β Β  └── index.html         <--------- Pre-rendered content
β”œβ”€β”€ assets
β”‚Β Β  β”œβ”€β”€ assetMap.json
β”‚Β Β  β”œβ”€β”€ my-app-0d31988c08747007cb982909a0b2c9db.css
β”‚Β Β  β”œβ”€β”€ my-app-a72732b0d2468246920fa5401610caf4.js
β”‚Β Β  β”œβ”€β”€ my-app-fastboot-af717865dadf95003aaf6903aefcd125.js
β”‚Β Β  β”œβ”€β”€ vendor-553c722f80bed2ea90c42b2c6a54238a.js
β”‚Β Β  └── vendor-9eda64f0de2569c64ba0d33f08940fbf.css
β”œβ”€β”€ contact
β”‚Β Β  └── index.html         <--------- Pre-rendered content
β”œβ”€β”€ index.html             <--------- Rewritten with pre-rendered content
β”œβ”€β”€ package.json
└── robots.txt

The resulting application can be served entirely statically, like a normal Ember app. But it has the fast-first-paint and SEO benefits of a Fastboot-rendered application for all of the URLs that you pre-rendered.

Configuring Your Webserver

Your webserver needs to do two things correctly for this to work:

  1. It should use a file like about/index.html to respond to URLs like /about. This is a pretty normal default behavior.
  2. It should use _empty.html to respond to unknown URLs (404s). In a normal Ember app, you would configure index.html here instead, but we may have already overwritten index.html with content that only belongs on the homepage, not on every route. This is why prember gives you a separate _empty.html file with no prerendered content.

Options

You pass options to prember by setting them in ember-cli-build.js:

// In ember-cli-build.js
let app = new EmberApp(defaults, {
  prember: {
    urls: [
      '/',
      '/about',
      '/contact'
    ]
  }
});

The supported options are:

  • urls: this can be an array or a promise-returning function that resolves to an array. How you generate the list of URLs is up to you, there are many valid strategies. See next section about using a custom url discovery function.
  • enabled: defaults to environment === 'production' so that prember only runs during production builds.
  • indexFile: defaults to "index.html". This is the name we will give to each of the files we create during pre-rendering.
  • emptyFile: defaults to "_empty.html". This is where we will put a copy of your empty index.html as it was before any pre-rendering.

Using a custom URL discovery function

If you pass a function as the urls option, prember will invoke it like:

let listOfUrls = await yourUrlFunction({ distDir, visit });

distDir is the directory containing your built application. This allows your function to inspect the build output to discover URLs.

visit is an asynchronous function that takes a URL string and resolves to a response from a running fastboot server. This lets your function crawl the running application to discover URLs.

For an example of both these strategies in action, see ./node-tests/url-tester.js in this repo's test suite.

Using prember in development

In addition to the enabled option, you can temporarily turn prember on by setting the environment variable PREMBER=true, like:

PREMBER=true ember serve

However, by default ember-cli doesn't understand that it should use a file like about/index.html to respond to a URL like /about. So you should do:

ember install prember-middleware

It's harmless to keep prember-middleware permanently installed in your app, it has no impact on your production application.

When running in development, you will see console output from ember-cli that distinguishes whether a given page was handled by prember vs handled on-the-fly by fastboot:

prember: serving prerendered static HTML for /about       <--- served by prember
2017-10-27T05:25:02.161Z 200 OK /some-other-page          <--- served by fastboot

Using prember from an addon

Addon authors may declare urls for prember during compilation. To do so, you will want to:

  • Add prember-plugin to your addon's package.json keywords array;
    • Consider also using package.json's ember-addon object to configure your addon to run before: 'prember'
  • Define a urlsForPrember(distDir, visit) function in your addon's main file;
    • This function shares an interface with the "custom URL discovery" function, as defined above; and
  • Advise your addon's users to install & configure prember in the host application.

Addon authors may also get access to urls from prember. To do so, you will want to:

  • Add prember-plugin to your addon's package.json keywords array;
    • Consider also using package.json's ember-addon object to configure your addon to run before: 'prember'
  • Define a urlsFromPrember(urls) function in your addon's main file;
    • This function will receive the array of urls prember knows about as the only argument; and
  • Advise your addon's users to install & configure prember in the host application.

Using prember with Embroider

You can use prember in an Embroider-based build, however you must apply some changes to your ember-cli-build.js for it to work. Embroider does not support the postprocessTree (type all) hook that this addon uses to implicitly hook into the build pipeline. But it exposes a prerender function to do so explicitly.

In a typical Embroider setup, your ember-cli-build.js will look like this:

const { Webpack } = require('@embroider/webpack');
return require('@embroider/compat').compatBuild(app, Webpack);

For prember to add its prerendered HTML pages on top of what Embroider already emitted, wrap the compiled output with the prerender function like this:

const { Webpack } = require('@embroider/webpack');
- return require('@embroider/compat').compatBuild(app, Webpack);
+ const compiledApp = require('@embroider/compat').compatBuild(app, Webpack);
+ 
+ return require('prember').prerender(app, compiledApp);

Deployment

You shouldn't need to do much special -- just make sure the html files get copied along with all your other files.

If you're using ember-cli-deploy-s3, you just need to customize the filePattern setting so it includes .html files. For example:

    ENV.s3 = {
      bucket: 'cardstack.com',
      region: 'us-east-1',
      filePattern: '**/*.{js,css,png,gif,ico,jpg,map,xml,txt,svg,swf,eot,ttf,woff,woff2,otf,html}'
      allowOverwrite: true
    };

Compared to other addons

There are other ways to pre-render content:

  • ember-prerender depends on having a real browser to do prerendering, which is heavy and complex. It's old and unmaintained.
  • ember-cli-prerender uses Fastboot like we do, but it is not integrated with the build pipeline (so it's harder to make it Just Workβ„’ with things like ember-cli-deploy) and it has stronger opinions about what URLs it will discover, including blueprint-driven sitemap configuration.
  • ember-cli-staticboot is quite similar to this addon, and I didn't realize it existed before I started making this one. I do think prember does a better job of integrating the static build output with the existing ember app in a way that requires the minimal webserver configuration.

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

More Repositories

1

ember-elsewhere

Render elsewhere in the DOM, with nice composition, animation, etc. Based on 100% public API.
JavaScript
184
star
2

ember-browserify

ember-cli addon for easily loading CommonJS packages from npm via browserify.
JavaScript
172
star
3

ember-animation-demo

A presentation at EmberConf 2014
CSS
129
star
4

ember-code-snippet

An Ember component for rendering pretty code snippets.
JavaScript
84
star
5

mho

Experimental service-worker based build system
TypeScript
76
star
6

memory-scroll

An Ember component that remembers its scroll position
JavaScript
57
star
7

ember-sidebars

A sidebar manager for Ember apps.
JavaScript
45
star
8

ember-handoff

Ember Engine for mounting and enhancing server-rendered web pages.
JavaScript
44
star
9

cardstack-editor

CSS
42
star
10

living-animation

Interactive EmberConf 2018 Slides
JavaScript
42
star
11

ember-set-body-class

Fastboot-compatible, template-driven helper for setting classes on the body
JavaScript
38
star
12

ember-giftwrap

A tool for packaging Ember addons
JavaScript
34
star
13

ember-cli-deploy-git

ember-cli-deploy plugin for uploading your built app to Git.
JavaScript
30
star
14

physical-design-demo

A demo app showing motion design in an Ember app. EmberConf 2015.
CSS
29
star
15

tracked-params

TypeScript
22
star
16

ember-cli-deploy-appshell

applicationCache-based shell for loading Ember apps
JavaScript
22
star
17

relativity

Interval tree clocks for Javascript
JavaScript
19
star
18

ember-resource-metadata

Per-resource metadata for ember-data
JavaScript
15
star
19

fast-sourcemap-concat

Fast sourcemap generation & concatenation.
JavaScript
13
star
20

ember-sass-bootstrap

Use the parts of bootstrap-sass that you want, and none that you don't.
JavaScript
13
star
21

ember-overlays

Annotated overlays for Ember apps.
JavaScript
12
star
22

ember-ignore-children-helper

Helps you ignore events that didn't directly target your own element.
JavaScript
11
star
23

ember-root-url

A template helper to keep your URLs relative to the app's rootURL.
JavaScript
10
star
24

ember-react-example

Example of invoking React components from an Ember app.
JavaScript
10
star
25

ember-data-relationship-tracker

JavaScript
9
star
26

pdfkit-www

In-browser PDF generation
Ruby
9
star
27

better-mocha-html-reporter

JavaScript
7
star
28

broccoli-sourcemap-concat

Fast, caching broccoli filter that generates & propagates source maps.
JavaScript
7
star
29

drupal-ember-dev

Drupal module that supports Ember development
PHP
7
star
30

xrouter

Home for Ember Polaris Routing experimentation.
JavaScript
6
star
31

emberconf-2017-demo

JavaScript
6
star
32

ember-head

Fastboot-compatible component for rendering content into <head>
JavaScript
6
star
33

are-my-node-modules-messed-up

It's a question worth asking.
JavaScript
6
star
34

ember-computed-cleanup

Ember computed properties with cleanup.
JavaScript
5
star
35

lazy-load-highcharts-demo

Code that accompanies a screencast.
JavaScript
4
star
36

prember-crawler

A web crawler for discovering URLs you want to pre-render with prember
JavaScript
4
star
37

ember-strict-warnings

Make warnings into errors during dev and test.
JavaScript
4
star
38

linkedin-demo

A demo I gave at LinkedIn.
HTML
4
star
39

initial-render-perf

A test scenario for Ember initial rendering performance
JavaScript
4
star
40

babel-import-util

Utility for manipulating imports within babel plugins
TypeScript
3
star
41

demonstrate-pdfjs

How to use pdfjs in an Ember app
JavaScript
3
star
42

ember-css-url

A helper for safely embedding URLs in style properties
JavaScript
3
star
43

ember-cli-proxy

JavaScript
3
star
44

build-if.macro

TypeScript
3
star
45

debugify

Exposes all browserify modules for console debugging (like requireify but different).
CoffeeScript
2
star
46

learning-demo

Demo to accompany a talk at EmberNYC, March 2018
JavaScript
2
star
47

flip-demo

Animation demos to accompany a presentation.
JavaScript
2
star
48

jan2020-meetup-example

Example code for a presentation at Boston Ember, January 2020.
JavaScript
2
star
49

optimist

An alternative model layer for Sproutcore.
1
star
50

distributed-ember-presentation

A presentation at WickedGoodEmber 2014.
CSS
1
star
51

tailwind-demo

How to integrate TailwindCSS into an Ember App
JavaScript
1
star
52

embroider-resolver-demo

Quick demo of how the Embroider resolver API works
JavaScript
1
star
53

koa-better-route

Like koa-route, except it works with koa-compose
JavaScript
1
star