Add HandlebarsTemplatePlugin for rewriting asset urls. (#38)

* Add HandlebarsTemplatePlugin and asset helper.

* Recompile on template change.
This commit is contained in:
Robert Long 2017-12-04 16:18:10 -08:00 коммит произвёл GitHub
Родитель 3361464c8f
Коммит b12cf55789
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 199 добавлений и 27 удалений

5
.gitignore поставляемый
Просмотреть файл

@ -58,4 +58,7 @@ typings/
.env
# webpack bundle
public/app.bundle.js*
public/*.bundle.js*
public/*.html
.DS_Store

Просмотреть файл

@ -1,13 +1,44 @@
# Mozilla Social Mixed Reality Client
A prototype client demonstrating a multi-user experience in WebVR. Built with [A-Frame](https://github.com/aframevr/aframe/)
A prototype client demonstrating a multi-user experience in WebVR. Built with
[A-Frame](https://github.com/aframevr/aframe/)
## Getting Started
To run the social client, type:
To run the social client, run:
```
```sh
git clone https://github.com/mozilla/mr-social-client.git
yarn install
yarn run dev
yarn start
```
## Building Static Files
To bundle javascript and generate the html templates, run:
```sh
yarn build
```
### Using CDN Assets
If you are hosting your static assets at separate path from the html documents,
the asset handlebars helper supports rewriting the base asset paths. To use it
run:
```sh
BASE_ASSETS_PATH="https://cdn.mysite.com/assets/" yarn build
```
Ex.
```hbs
<img src="{{asset "asseturl.png"}}"/>
```
Will become:
```html
<img src="https://cdn.mysite.com/assets/asseturl.png?c=1512428142413"/>
```

Просмотреть файл

@ -4,8 +4,9 @@
"main": "src/index.js",
"license": "MPL-2.0",
"scripts": {
"start": "npm run dev",
"dev": "webpack-dev-server --https --host 0.0.0.0 --useLocalIp --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"build": "NODE_ENV='production' webpack --config webpack.prod.js",
"prettier": "prettier --write src/**/*.js"
},
"dependencies": {
@ -30,10 +31,13 @@
"babel-minify-webpack-plugin": "^0.2.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"chokidar": "^1.7.0",
"css-loader": "^0.28.7",
"eslint": "^4.10.0",
"eslint-config-prettier": "^2.6.0",
"eslint-plugin-prettier": "^2.3.1",
"fs-extra": "^4.0.2",
"handlebars": "^4.0.11",
"prettier": "^1.7.0",
"style-loader": "^0.19.0",
"webpack": "^3.6.0",

Просмотреть файл

@ -0,0 +1,69 @@
const Handlebars = require("handlebars");
const fs = require("fs-extra");
const path = require("path");
const chokidar = require("chokidar");
class HandlebarsTemplatePlugin {
constructor(options) {
this.templatesPath = options.templatesPath;
this.templateExtension = options.templateExtension || ".hbs";
this.templateOptions = options.templateOptions || {};
if (options.helpers) {
Object.keys(options.helpers).forEach(helperName => {
Handlebars.registerHelper(helperName, options.helpers[helperName]);
});
}
}
apply(compiler) {
compiler.plugin("watch-run", (compilation, callback) => {
chokidar
.watch(path.join(this.templatesPath, "*" + this.templateExtension))
.on("change", () => {
compiler.run(err => {
if (err) {
throw err;
}
});
});
callback();
});
compiler.plugin("emit", (compilation, callback) => {
this.compileTemplates(compiler, compilation).then(callback);
});
}
// Compile all handlebars templates in the template directory and place them in the output directory.
async compileTemplates(compiler, compilation) {
const outputPath = compiler.options.output.path;
const templateFiles = await fs.readdir(this.templatesPath);
const templatePromises = templateFiles
.filter(filename => filename.indexOf(this.templateExtension) !== -1)
.map(fileName => {
const filePath = path.join(this.templatesPath, fileName);
const outputFileName = fileName.replace(
this.templateExtension,
".html"
);
const outputFilePath = path.join(outputPath, outputFileName);
return this.compileTemplate(filePath, outputFilePath);
});
await Promise.all(templatePromises);
}
// Compile a single handlebars template given a file path and output file path.
async compileTemplate(filePath, outputFilePath) {
const templateStr = await fs.readFile(filePath);
const template = Handlebars.compile(templateStr.toString());
const compiledStr = template(this.templateOptions);
return fs.writeFile(outputFilePath, compiledStr);
}
}
module.exports = HandlebarsTemplatePlugin;

Просмотреть файл

@ -12,6 +12,6 @@
</head>
<body>
<div id="root"></div>
<script src="./lobby.bundle.js"></script>
<script src="{{asset "lobby.bundle.js"}}"></script>
</body>
</html>

Просмотреть файл

@ -3,7 +3,7 @@
<head>
<title>Mozilla Mixed Reality Social Client</title>
<script src="https://webrtc.github.io/adapter/adapter-6.0.2.js"></script>
<script src="./app.bundle.js"></script>
<script src="{{asset "app.bundle.js" }}"></script>
<style>
.a-enter-vr {
top: 90px;
@ -14,7 +14,7 @@
width: 100vw;
height: 100vh;
z-index: 10001;
background: #eaeaea no-repeat url(assets/loading.gif) center center;
background: #eaeaea no-repeat url({{asset "assets/loading.gif" }}) center center;
opacity: 0.9;
}
</style>
@ -32,19 +32,16 @@
light="defaultLightsEnabled: false">
<a-assets>
<img id="grid" src="assets/grid.png" crossorigin="anonymous" />
<img id="sky" src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg" crossorigin="anonymous" />
<a-asset-item id="bot-head-mesh" src="{{asset "assets/avatars/Bot_Head_Mesh.glb" }}"></a-asset-item>
<a-asset-item id="bot-body-mesh" src="{{asset "assets/avatars/Bot_Body_Mesh.glb" }}"></a-asset-item>
<a-asset-item id="bot-left-hand-mesh" src="{{asset "assets/avatars/Bot_LeftHand_Mesh.glb" }}"></a-asset-item>
<a-asset-item id="bot-right-hand-mesh" src="{{asset "assets/avatars/Bot_RightHand_Mesh.glb"}}"></a-asset-item>
<a-asset-item id="bot-head-mesh" src="assets/avatars/Bot_Head_Mesh.glb"></a-asset-item>
<a-asset-item id="bot-body-mesh" src="assets/avatars/Bot_Body_Mesh.glb"></a-asset-item>
<a-asset-item id="bot-left-hand-mesh" src="assets/avatars/Bot_LeftHand_Mesh.glb"></a-asset-item>
<a-asset-item id="bot-right-hand-mesh" src="assets/avatars/Bot_RightHand_Mesh.glb"></a-asset-item>
<a-asset-item id="watch-model" src="{{asset "assets/hud/watch.gltf"}}"></a-asset-item>
<a-asset-item id="watch-model" src="assets/hud/watch.gltf"></a-asset-item>
<a-asset-item id="meeting-space1-mesh" src="assets/environments/MeetingSpace1_mesh.glb"></a-asset-item>
<a-asset-item id="outdoor-facade-mesh" src="assets/environments/OutdoorFacade_mesh.glb"></a-asset-item>
<a-asset-item id="floor-nav-mesh" src="assets/environments/FloorNav_mesh.glb"></a-asset-item>
<a-asset-item id="meeting-space1-mesh" src="{{asset "assets/environments/MeetingSpace1_mesh.glb"}}"></a-asset-item>
<a-asset-item id="outdoor-facade-mesh" src="{{asset "assets/environments/OutdoorFacade_mesh.glb"}}"></a-asset-item>
<a-asset-item id="floor-nav-mesh" src="{{asset "assets/environments/FloorNav_mesh.glb"}}"></a-asset-item>
<!-- Templates -->
<script id="head-template" type="text/html">
@ -144,7 +141,7 @@
>
<a-entity
id="watch"
cached-gltf-model="assets/hud/watch.gltf"
cached-gltf-model="#watch-model"
position="0 0.0015 0.147"
rotation="3.5 0 0"
>

Просмотреть файл

@ -1,4 +1,6 @@
const path = require("path");
const HandlebarsTemplatePlugin = require("./templates/HandlebarsTemplatePlugin");
const Handlebars = require("handlebars");
module.exports = {
entry: {
@ -12,7 +14,7 @@ module.exports = {
module: {
rules: [
{
test: /.js$/,
test: /\.js$/,
include: [path.resolve(__dirname, "src")],
exclude: [path.resolve(__dirname, "node_modules")],
loader: "babel-loader"
@ -22,5 +24,28 @@ module.exports = {
use: ["style-loader", "css-loader"]
}
]
}
},
plugins: [
new HandlebarsTemplatePlugin({
templatesPath: path.resolve(__dirname, "templates"),
helpers: {
/**
* Register a handlebars helper that prepends the base asset path.
* Useful for things like placing assets on a CDN and cache busting.
* Example:
* input: <img src="{{asset "asset.png"}}"/>
* output: <img src="https://cdn.mysite.com/asset.png?c="/>
*/
asset: assetPath => {
const isProd = process.env.NODE_ENV === "production";
const baseAssetsPath = process.env.BASE_ASSETS_PATH || "/";
const cacheBustQueryString = isProd ? "?c=" + Date.now() : "";
const url = baseAssetsPath + assetPath + cacheBustQueryString;
return new Handlebars.SafeString(url);
}
}
})
]
};

Просмотреть файл

@ -306,7 +306,7 @@ async@0.2.x:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
async@^1.5.2:
async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@ -2620,6 +2620,14 @@ fs-exists-sync@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
fs-extra@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -2780,7 +2788,7 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
graceful-fs@^4.1.2:
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@ -2788,6 +2796,16 @@ handle-thing@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
handlebars@^4.0.11:
version "4.0.11"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
dependencies:
async "^1.4.0"
optimist "^0.6.1"
source-map "^0.4.4"
optionalDependencies:
uglify-js "^2.6"
har-schema@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
@ -3343,6 +3361,12 @@ json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
optionalDependencies:
graceful-fs "^4.1.6"
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
@ -3678,6 +3702,10 @@ minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@ -3945,6 +3973,13 @@ opn@^5.1.0:
dependencies:
is-wsl "^1.1.0"
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
minimist "~0.0.1"
wordwrap "~0.0.2"
optionator@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
@ -5136,7 +5171,7 @@ source-map-support@^0.4.15:
dependencies:
source-map "^0.5.6"
source-map@^0.4.2:
source-map@^0.4.2, source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
@ -5530,7 +5565,7 @@ ua-parser-js@^0.7.9:
version "0.7.17"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
uglify-js@^2.8.29:
uglify-js@^2.6, uglify-js@^2.8.29:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
dependencies:
@ -5577,6 +5612,10 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
universalify@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@ -5812,6 +5851,10 @@ wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"