Skip to content

Commit

Permalink
Merge branch 'GPII-4100'
Browse files Browse the repository at this point in the history
* GPII-4100:
  GPII-4100: Updated for compatibility with a newer "potentia ii" version of infusion that doesn't allow options munging.
  GPII-4100: Updated to newest chokidar after corresponding with author re: an OS X issue that is now apparently fixed.
  GPII-4100: Improved fix for chokidar FSevents errors on OS X.
  GPII-4100: Updated to latest chokidar to hopefully address EPERM issues.
  GPII-4100: temporary fix for windows Firefox test timeouts.
  GPII-4100: Fixed lingering problems with template ordering.
  GPII-4100: Updated lingering template directory options arrays.
  GPII-4132: GPII-4185: Standardised directory handling for messages and templates to allow proper prioritisation.
  GPII-4100: Fixed bug in message helper key handling that would choke on keys with dots.
  GPII-4100: Reordered node test rollup to avoid cross-talk between renderer and dispatcher tests.
  GPII-4100: Refactored to use recent potentia-ii, which required rewriting the `getMessage` helper and i18n stack.
  GPII-4100: Updated to latest potentia-ii infusion and linting.  Reverted test workarounds and fixed linting errors.
  • Loading branch information
amb26 committed Nov 25, 2019
2 parents c511fc0 + 2c6f7a2 commit bd092b0
Show file tree
Hide file tree
Showing 45 changed files with 374 additions and 304 deletions.
8 changes: 7 additions & 1 deletion docs/handlebars.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ fluid.defaults("my.grade.name", {
hb: {
type: "gpii.express.hb",
options: {
templateDirs: ["%gpii-handlebars/src/templates", "%my-package/src/templates"]
templateDirs: {
handlebars: "%gpii-handlebars/src/templates",
myPackage: {
path: "%my-package/src/templates",
priority: "before:handlebars"
}
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Belgium). It is recommended to provide not only variations, but a default for t
filename `myPackageMessages-nl.json` for Netherlands Dutch and `myPackageMessages-nl_BE.json` for Flemish. In this
manner, we can work with either a full locale (`nl_BE`) or simply a language prefix (`nl`).

### `gpii.handlebars.i18n.messageLoader`
### `gpii.handlebars.i18n.messageBundleLoader`

As there are multiple components that need access to the same "bundle of message bundles", a common component is
provided that loads all bundles from the filesystem. This component supports the following configuration options:
Expand Down Expand Up @@ -253,7 +253,7 @@ To simplify the browser side usage, a piece of `gpii.express` middleware is prov
[language headers provided by the browser request](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language),
and follows the failover strategy outlined above. You are expected to populate this component's `messageBundles` member
with individual message bundles, keyed by language or locale. In most cases you will want to use the
`gpii.handlebars.i18n.messageLoader` grade described above to populate this.
`gpii.handlebars.i18n.messageBundleLoader` grade described above to populate this.

## Browser Usage

Expand Down
13 changes: 8 additions & 5 deletions docs/renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fluid.defaults("my.localisedRenderer.component", {

var renderer = my.localisedRenderer.component();

console.log(renderer.render("localisedPage", { key: "hello-message-key", mood: "variable"})); // logs `Hello, variable world.`
fluid.log(renderer.render("localisedPage", { key: "hello-message-key", mood: "variable"})); // logs `Hello, variable world.`

```

Expand Down Expand Up @@ -84,7 +84,7 @@ fluid.defaults("my.renderer.component", {

var renderer = my.renderer.component();

console.log(renderer.renderWithLayout("myPage", { myVariable: "my value" }));
fluid.log(renderer.renderWithLayout("myPage", { myVariable: "my value" }));

/*
Expand Down Expand Up @@ -217,6 +217,9 @@ package, with the exception of the `initBlock` helper used within the view engin
## Internationalisation and Localisation

The renderer includes the [messageHelper helper](i18n.md), which can be used to internationalise and localise template
content. In order for the renderer to have access to the necessary message templates, you are expected to populate this
component's `messages` member with a single message bundle. In most cases you will want to use the
`gpii.handlebars.i18n.messageLoader` grade described [in the i18n documentation](i18n.md) to populate this.
content. On the client side, this should be populated by the ["Server Resource Aware"](server-resource-aware.md) grade.

On the server side, you will need to ensure that the renderer and handlebars itself have access to the full range of
available message bundles in their `model.messageBundles`. There is a ["message bundle loader"](i18n.md) provided for this
purpose. Once the renderer, handlebars, etc. has this set of bundles, the specific language bundle is determined per
request based on the `locale` query parameter, or if that is not found, the `Accept-Language` HTTP request header.
16 changes: 16 additions & 0 deletions docs/server-resource-aware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# `gpii.handlebars.serverResourceAware`

This component uses the "resource loading" features of the Infusion framework to load the templates and message bundles
provided by the middleware in this package. Once both are available, a client-side [renderer component](renderer.md)
is created. The `afterRenderer` event is fired once the renderer is available.

For more examples of using this in depth, check out the `templateFormControl` grade or the client side tests.

## Component Options

This component has two options, that control how its resources are loaded.

| Option | Type | Description |
| --------------------- | ---------- | ----------- |
| `resources.message` | `{Object}` | This is configured to load message bundles from `/messages` on the same hostname/port as the page itself uses. See [the "Resource Loader" documentation](https://github.com/amb26/infusion-docs/blob/FLUID-6148/src/documents/ResourceLoader.md) for details on how to configure this option. |
| `resources.templates` | `{Object}` | This is configured to load message bundles from `/templates` on the same hostname/port as the page itself uses. See [the "Resource Loader" documentation](https://github.com/amb26/infusion-docs/blob/FLUID-6148/src/documents/ResourceLoader.md) for details on how to configure this option. |
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ require("./src/js/server/inline");
require("./src/js/server/inlineMessageBundlingMiddleware");
require("./src/js/server/lib/i18n-node");
require("./src/js/server/md-server");
require("./src/js/server/messageHelper");
require("./src/js/server/singleTemplateMiddleware");
require("./src/js/server/standaloneRenderer");
19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@
"posttest": "node node_modules/nyc/bin/nyc.js report --reporter text-summary --reporter html"
},
"dependencies": {
"chokidar": "2.1.8",
"gpii-binder": "1.0.5",
"chokidar": "3.3.0",
"gpii-binder": "1.0.6",
"gpii-express": "1.0.15",
"handlebars": "4.3.1",
"infusion": "3.0.0-dev.20190930T135848Z.bfc072169.FLUID-6148",
"json5": "2.1.0",
"handlebars": "4.5.3",
"infusion": "3.0.0-dev.20191123T235705Z.4cd1b9d90.FLUID-6426",
"json5": "2.1.1",
"markdown-it": "10.0.0",
"md5": "2.2.1",
"underscore-node": "0.1.2"
},
"devDependencies": {
"cheerio": "1.0.0-rc.3",
"eslint": "6.4.0",
"eslint-config-fluid": "1.3.0",
"eslint": "6.7.1",
"eslint-config-fluid": "1.4.0",
"gpii-grunt-lint-all": "1.0.5",
"gpii-testem": "2.1.10",
"graceful-fs": "4.2.2",
"gpii-testem": "2.1.11",
"graceful-fs": "4.2.3",
"grunt": "1.0.4",
"kettle": "1.11.0",
"mkdirp": "0.5.1",
Expand All @@ -37,6 +37,7 @@
"recursive-copy": "2.0.10",
"request": "2.88.0",
"rimraf": "3.0.0",
"testem": "3.0.1",
"tough-cookie": "3.0.1"
}
}
18 changes: 18 additions & 0 deletions src/js/client/messageHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* The client-side overlay for the i18n "message helper" used in rendering {{message}} content. */
/* eslint-env node */
"use strict";
var fluid = fluid || require("infusion");

fluid.defaults("gpii.handlebars.helper.messageHelper.client", {
gradeNames: ["gpii.handlebars.helper.messageHelper"],
helperName: "messageHelper",
model: {
messages: {}
},
invokers: {
"resolveMessage": {
funcName: "gpii.handlebars.helper.messageHelper.resolveMessage",
args: ["{that}.model.messages", "{arguments}.0", "{arguments}.1", "{arguments}.2"] // messages, messageKey, dataOrRootContext, rootContext
}
}
});
12 changes: 10 additions & 2 deletions src/js/client/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@
fluid.defaults("gpii.handlebars.renderer", {
gradeNames: ["gpii.handlebars.renderer.common"],
model: {
messageBundles: {},
templates: {}
templates: {},
messages: {}
},
components: {
"md": {
"type": "gpii.handlebars.helper.md.client"
},
messageHelper: {
type: "gpii.handlebars.helper.messageHelper.client",
options: {
model: {
messages: "{gpii.handlebars.renderer.common}.model.messages"
}
}
}
},
invokers: {
Expand Down
27 changes: 4 additions & 23 deletions src/js/client/serverResourceAware.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,9 @@
/*
A base grade for components that make use of the Handlebars client to render their content. To use this in a
component, you will need to:
A grade that loads all required templates and message bundles, then creates a client-side renderer.
1. Pass in a `templateUrl` option that points to a REST interface from which the template content can be retrieved.
See the `inline` server-side component for an example of the output required.
For an example of using this in depth, check out the provided `templateFormControl` grade or the client side tests.
2. Replace the `renderInitialMarkup` invoker with your own implementation, that should replace selected view content
using the `templates` component. Replace this with an empty function if you want to disable the initial render
once template content is loaded.
This grade provides a convenience invoker that you can use when defining your `renderMarkup` controller, as in:
renderInitialMarkup: {
func: "{that}.renderMarkup",
args: [
"{that}",
"{that}.options.selectors.selector",
"{that}.options.templateKey",
"{that}.model",
"appendTo"
]
}
For an example of using this in depth, check out the provided `templateFormControl` grade or the client side tests.
*/
/* eslint-env browser */
(function (fluid) {
Expand All @@ -45,8 +25,9 @@
}
},
model: {
// TODO: Review after it's possible to drill deeper into "parsed" material.
messages: "{that}.resources.messages.parsed",
templates: "{that}.resources.templates.parsed.templates"
templates: "{that}.resources.templates.parsed"
},
components: {
renderer: {
Expand Down
4 changes: 2 additions & 2 deletions src/js/client/templateAware.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
gradeNames: ["gpii.handlebars.templateAware"],
model: {
templates: "{that}.options.templates",
messageBundles: {}
messages: {}
},
mergePolicy: {
"templates.layouts": "noexpand",
Expand All @@ -105,7 +105,7 @@
},
model: {
templates: "{gpii.handlebars.templateAware.standalone}.model.templates",
messageBundles: "{gpii.handlebars.templateAware.standalone}.model.messageBundles"
messages: "{gpii.handlebars.templateAware.standalone}.model.messages"
}
}
}
Expand Down
43 changes: 35 additions & 8 deletions src/js/common/messageHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,57 @@ var gpii = fluid.registerNamespace("gpii");

fluid.registerNamespace("gpii.handlebars.helper.messageHelper");

gpii.handlebars.helper.messageHelper.getHelper = function () {
fluid.registerNamespace("gpii.handlebars.helper.messageHelper");

/**
*
* Look up and render a single i18n string template ("message"). Used by both the client and server implementation.
*
* @param {Object} messages - A map of i18n keys and string templates.
* @param {String} messageKey - The i18n key for our specific message.
* @param {Object} dataOrRootContext - Depending on how Handlebars is called, either a data payload, or the root context.
* @param {Object} rootContext - If the helper is called with three arguments, this will be the root context.
* @return {String} - The plain text of the i18n template with all variable references replaced with data.
*
*/
gpii.handlebars.helper.messageHelper.resolveMessage = function (messages, messageKey, dataOrRootContext, rootContext) {
// If we have a third argument, then the second argument is our "data". Otherwise, we use the root context (equivalent to passing "." as the variable).
var data = rootContext ? dataOrRootContext : fluid.get(dataOrRootContext, "data.root");

// Use the array notation to avoid problems with namespaced keys, i.e. `my.package.i18n.mykey`
var messageTemplate = fluid.get(messages, [messageKey]);
if (messageTemplate) {
return fluid.stringTemplate(messageTemplate, data);
}
else {
return "[Message string for key " + messageKey + " not found]";
}
};

gpii.handlebars.helper.messageHelper.getHelper = function (that) {
return function (messageKey, dataOrRootContext, rootContext) {
if (arguments.length < 2) {
fluid.fail("You must call the 'messageHelper' helper with at least a message key.");
}
else {
// Pick up the message bundles from the root context, which is always the last argument.
var messages = fluid.get(rootContext || dataOrRootContext, "data.root.messages");

// If we have a third argument, then the second argument is our "data". Otherwise, we use the root context (equivalent to passing "." as the variable).
var data = rootContext ? dataOrRootContext : fluid.get(dataOrRootContext, "data.root");
var resolver = fluid.messageResolver({ messageBase: messages });
return resolver.resolve(messageKey, data);
return that.resolveMessage(messageKey, dataOrRootContext, rootContext);
}
};
};

fluid.defaults("gpii.handlebars.helper.messageHelper", {
gradeNames: ["gpii.handlebars.helper"],
helperName: "messageHelper",
model: {
messages: {}
},
invokers: {
"getHelper": {
"funcName": "gpii.handlebars.helper.messageHelper.getHelper",
"args": ["{that}"]
},
"resolveMessage": {
funcName: "fluid.notImplemented"
}
}
});
9 changes: 2 additions & 7 deletions src/js/common/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,11 @@ var Handlebars = Handlebars || {};
* @return {String} The renderered content.
*/
gpii.handlebars.renderer.common.render = function (that, templateKey, context) {
var combinedContext = fluid.extend(true, { messages: that.model.messages}, context);

var template = fluid.get(that.model, ["templates", "pages", templateKey]);
if (template) {
var compiledTemplate = Handlebars.compile(template);

return compiledTemplate(combinedContext);
return compiledTemplate(context);
}
else {
fluid.fail("Renderer can't find template '" + templateKey + "'.");
Expand All @@ -106,7 +104,7 @@ var Handlebars = Handlebars || {};
var pageBody = that.render(templateKey, context);

// Pass both the body and the derived message bundle as part of the effective context.
var layoutContext = fluid.extend(true, { messages: that.model.messages}, { body: pageBody}, context);
var layoutContext = fluid.extend(true, {}, { body: pageBody}, context);

// Render the page body in the selected layout (or the default if none is selected).
var layoutTemplateKey = context.layout ? context.layout : that.options.defaultLayout;
Expand Down Expand Up @@ -148,9 +146,6 @@ var Handlebars = Handlebars || {};
},
jsonify: {
type: "gpii.handlebars.helper.jsonify"
},
messageHelper: {
type: "gpii.handlebars.helper.messageHelper"
}
},
invokers: {
Expand Down
2 changes: 1 addition & 1 deletion src/js/server/dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var path = require("path");

gpii.handlebars.dispatcherMiddleware.middleware = function (that, req, res, next) {
var templateName = req.params.template ? req.params.template : that.options.defaultTemplate;
var resolvedTemplateDirs = gpii.express.hb.resolveAllPaths(that.options.templateDirs);
var resolvedTemplateDirs = gpii.handlebars.resolvePrioritisedPaths(that.options.templateDirs);

var templateExists = fluid.find(resolvedTemplateDirs, gpii.express.hb.getPathSearchFn(["pages", templateName + ".handlebars"]));
if (templateExists) {
Expand Down
8 changes: 7 additions & 1 deletion src/js/server/handlebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ gpii.express.hb.engine = function (that, templatePath, templateContext, callback
};

gpii.express.hb.configureExpress = function (that, expressComponent) {
var resolvedTemplateDirs = gpii.express.hb.resolveAllPaths(that.options.templateDirs);
var resolvedTemplateDirs = gpii.handlebars.resolvePrioritisedPaths(that.options.templateDirs);
if (resolvedTemplateDirs.length > 0) {
expressComponent.express.set("views", resolvedTemplateDirs);
expressComponent.express.engine("handlebars", that.engine);
Expand All @@ -46,11 +46,17 @@ fluid.defaults("gpii.express.hb", {
gradeNames: ["fluid.modelComponent"],
config: "{expressConfigHolder}.options.config",
namespace: "handlebars", // Namespace to allow other middleware to put themselves in the chain before or after us.
model: {
messageBundles: {}
},
components: {
renderer: {
type: "gpii.handlebars.standaloneRenderer",
options: {
templateDirs: "{gpii.express.hb}.options.templateDirs",
model: {
messageBundles: "{gpii.express.hb}.model.messages"
},
components: {
initBlock: {
type: "gpii.handlebars.helper.initBlock"
Expand Down
6 changes: 3 additions & 3 deletions src/js/server/inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ gpii.handlebars.inlineTemplateBundlingMiddleware.request.sendResponse = function
that.options.response.status(304).end();
}
else {
gpii.express.handler.sendResponse(that, that.options.response, 200, { ok: true, templates: that.options.templates });
gpii.express.handler.sendResponse(that, that.options.response, 200, that.options.templates);
}
}
else {
gpii.express.handler.sendResponse(that, that.options.response, 500, { ok: false, message: that.options.messages.noTemplates});
gpii.express.handler.sendResponse(that, that.options.response, 500, { isError: true, message: that.options.messages.noTemplates});
}
};

Expand All @@ -57,7 +57,7 @@ gpii.handlebars.inlineTemplateBundlingMiddleware.loadTemplates = function (that
that.templates[key] = {};
});

var resolvedTemplateDirs = gpii.express.hb.resolveAllPaths(that.options.templateDirs);
var resolvedTemplateDirs = gpii.handlebars.resolvePrioritisedPaths(that.options.templateDirs);
fluid.each(resolvedTemplateDirs, function (templateDir) {
// Start with each "views" directory and work our way down
var dirContents = fs.readdirSync(templateDir);
Expand Down
Loading

0 comments on commit bd092b0

Please sign in to comment.