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

Static site build process #802

Merged
merged 97 commits into from
Oct 29, 2017
Merged

Static site build process #802

merged 97 commits into from
Oct 29, 2017

Conversation

goldpbear
Copy link
Contributor

@goldpbear goldpbear commented Aug 20, 2017

Addresses: #655

This might actually be ready now! :)

Add static site build process. The approach here is to create standalone index.html files for each language for a given flavor. The final index.html files contain localized and serialized config data, localized and precompiled client-side Handlebars templates, and resolved flavor template inheritances.

All build work is performed in static-build.js. See that file for a heavily annotated explanation of the build process.

The build process handles the following, previously performed by Django:

  • config localization
  • config parsing and injection
  • jstemplates and pages localization
  • static asset management, including preservation of flavor override behavior
  • base and flavor template inheritance

It adds the following functionality:

  • jstemplates and pages precompilation

Known issues with the current build:

  • There's a scope resolution problem with one of the Handlebars templates, where the following expression in place-survey-form.html can't be evaluated: {{get_value ../../../.. name}}. This is preventing place detail views from rendering, and needs further investigation
  • Language switching is currently not implemented
  • The list view is busted--need to investigate
  • Social auth login functionality previously routed through the proxy needs to be moved wholesale to the api server
  • Notification email delivery needs to be ported to the api server

Other todos:

  • Better build process organization. Everything currently lives in webpack.config.js
  • Asynchronous processing (per @zmbc)
  • Incorporate base project .po files into localization process
  • Deployment environment details
  • When localizing, iterate over languages listed in the config, not over all .po files in the locale directory. This will save significant build work for some flavors.
  • DEBUG setting in base.hbs should probably correspond to the NODE_ENV setting.
  • static asset paths in configs have hard-coded static directories: e.g. /static/css/images/markers/marker-parks.png. We should refactor to something like {{STATIC_URL}}css/images/markers/marker-parks.png.
  • po file generation/updating (previously performed with a Django utility?)
  • font assets are not copied to www folder yet
  • move dataset urls to configs, perhaps with the option of overwriting urls with .env settings
  • use yaml-loader to watch and rebuild config files during development
  • update docs
  • in production build, read production api dataset urls from a json file packaged with other build files
  • hash bundle names

NOTE: Only the pboakland flavor has been refactored for the static site build. To test the build you'll need the following .env keys:

UPDATE: all flavors have been ported

To build the static site and start the dev server, run the following:

npm install
npm start

This will build only the en_US locale. To build all locales for production (or for testing a given locale), run:

npm run build

The build process adds dev dependencies on the following packages:

Copy link
Member

@modulitos modulitos left a comment

Choose a reason for hiding this comment

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

Great work! Thanks for doing this :-)

In general, I think we should separate out our handlebar and localization compilation scripts from the webpack.config.js, which I mentioned in my comment below. We can invoke that script within webpack.config.js.

Also, an additional TODO's that we may want to add:

  • Replicate our Django makemessages command that parses our templates for new translation strings and adds them to our .po files.

var path = require('path');
var glob = require('glob');
var fs = require('fs');
require("babel-polyfill")
var fs = require('fs-extra');
Copy link
Member

Choose a reason for hiding this comment

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

All of theses var's should be either let or const here, as this is running in node, which has ES6 support

// -----------------------------------------------------------------------------

var source, template;
var d = time(
Copy link
Member

Choose a reason for hiding this comment

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

It's a little confusing when variables are re-declared. Let's try to declare them once.

// Handlebars executable
var handlebarsExec = path.resolve(
__dirname,
"node_modules/handlebars/bin/handlebars"
Copy link
Member

Choose a reason for hiding this comment

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

You shouldn't have to reach into node_modules to access executables. Shouldn't the executable be available as handlebars?

Copy link
Member

Choose a reason for hiding this comment

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

Also, this variable is only used once, around line 333. If we're going to use this variable, I think it makes sense to declare/set it closer to where it's being used. I think it would make the code easier to follow.

});

// Helper for injecting precompiled templates to the index.html file
var compiledTemplatesOutputPath;
Copy link
Member

Choose a reason for hiding this comment

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

I'm a little confused here... this variable is already set above (line 101) - so what's the point of this declaration?

This ties into my earlier comment about declaring our variables multiple times.

);

// Localization
var baseLocaleDir = path.resolve(
Copy link
Member

Choose a reason for hiding this comment

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

Is this var being used?

__dirname,
"src/base/locale"
);
var flavorLocaleDir = path.resolve(
Copy link
Member

Choose a reason for hiding this comment

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

Same issue here - this var isn't being used until around line 245. So let's not set it until we need it.

};

// Timing
var time = function(fn) {
Copy link
Member

Choose a reason for hiding this comment

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

We should probably get rid of this timing function - I don't see the benefit of having it in the master branch. It's fine for taking benchmarks, but otherwise it's creating extra complexity through the use of callbacks.

Also, you can try making this an async function, and calling it with the await keyword. That should save you from using the callbacks.

// content into the index-xx.html file
// -----------------------------------------------------------------------------

fs.readdirSync(flavorLocaleDir).forEach((langDir) => {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should move this whole call into a separate script. The handlebars compilation can also be a separate script. It would make this webpack.config.js file easier to read and more modular. We'll need to do this anyway if we want to watch file changes and only run specific process of the static site build.

// Localize jstemplates
fs.readdirSync(outputJSTemplatesPath).forEach((template) => {
if (template.endsWith("html")) {
var templ = path.resolve(outputJSTemplatesPath, template);
Copy link
Member

Choose a reason for hiding this comment

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

Can we use a different variable name than templ here? It's kinda confusing as we already have a template variable

log("Finished copying jstemplates assets", d);

// Localize jstemplates
fs.readdirSync(outputJSTemplatesPath).forEach((template) => {
Copy link
Member

Choose a reason for hiding this comment

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

This is really confusing - there is already a template variable in the outer scope of this function. I suggest we should avoid shadowing it by naming our template variables to something more useful.

@goldpbear
Copy link
Contributor Author

Regarding certain static assets (CSS, JS, and Handlebars templates), I was thinking if possible we should try to host the base assets in one common S3 bucket and the flavor assets on each flavor bucket.

This would be easy with the CSS bundle, since we can achieve the flavor override behavior in the browser as long as we load each flavor's custom.css after the main CSS bundle. I'm hoping a similar thing could be achieved with the JS bundle and the precompiled Handlebars templates. I think it's possible with the JS bundle; I'm less certain about the templates.

But if we can achieve this structure, then updates to the base project CSS, JS, or templates will only need to trigger an update to a single S3 bucket. In the current build, on the other hand, base project updates would need to trigger a re-build on every single flavor to take full effect.

The situation is trickier with image assets. Because images and markers are referenced in multiple places (throughout the config and Handlebars templates), we would need to change image paths throughout several files to reference base assets stored in one place and flavor override assets stored on a given flavor bucket.

As part of our deployment, then, we might want some kind of git hook that detects if a change to an image directory was made, and triggers the appropriate update of image assets across S3 flavor buckets.

Just some thoughts...

zmbc and others added 22 commits October 17, 2017 10:43
- This commit sets up a static site build process designed to eliminate the need for
  the front-end Django server. The build handles localization, Handlebars
  precompilation, and static asset management
- The following dev dependecies are introduced:
  - recursive-copy
  - js-yaml
  - node-gettext
  - gettext-parser
  - object-walk
  - handlebars
  - wax-on

Addresses: #655
Localized versions of the index.html file are moved to locale directories and served
from there.
- convert index.html templates to Handlebars
- merge language_picker.html template into index.html
- convert en locale code to en-us in configs
- remove duplicate config keys
This is only totally working in production mode.
goldpbear and others added 28 commits October 23, 2017 10:43
Localized versions of the index.html file are moved to locale directories and served
from there.
- (follows on #806)
- put language html files in correct filename format
- fix language switching url paths
- fix various static asset filepaths
- remove url rewrites in webpack-dev-server (default 404 behavior catches everything)
- change outputBasePath to static/css/images to be consistent with existing paths
- var -> const/let
- move let/const declarations closer to where they're used
- other misc cleanup
- all stored paths are to the dev api
- add api_root path
- paths can be overridden using env variables using existing syntax
- use xgettext and msgmerge to update existing flavor translations
- generate updated po catalogs from flavor config, jstemplates, and pages files
- rename existing django.po files to more generic messages.po
Add makemessages functionality to static site build
- remove an __init__.py
- don't use old dist folder to store temp build files
# Conflicts:
#	package-lock.json
@goldpbear goldpbear merged commit 1f881c4 into master Oct 29, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants