diff --git a/packages/electrode-react-webapp/README.md b/packages/electrode-react-webapp/README.md
index 4e1e143ea3..840fa21b5c 100644
--- a/packages/electrode-react-webapp/README.md
+++ b/packages/electrode-react-webapp/README.md
@@ -7,8 +7,8 @@ a bootstrapping React application. With support for webpack dev server integrat
## Installing
-```
-npm install electrode-react-webapp --save
+```bash
+$ npm install electrode-react-webapp --save
```
## Usage
@@ -45,6 +45,11 @@ const config = {
"/{args*}": {
view: "index",
content: "
Hello React!
"
+ },
+ unbundledJS: {
+ enterHead: [
+ {src: "http://cdn.com/js/lib.js"}
+ ]
}
}
}
@@ -80,23 +85,26 @@ The current defaults are:
What you can do with the options:
- * `pageTitle` `(String)` The value to be shown in the browser's title bar
- * `webpackDev` `(Boolean)` whether to use webpack-dev-server's URLs for retrieving CSS and JS bundles.
- * `serverSideRendering` `(Boolean)` Toggle server-side rendering.
- * `htmlFile` `(String)` Absolute or relative path to the application root html file.
- It must contains the following placeholders:
- - `{{PAGE_TITLE}}` page title.
- - `{{WEBAPP_BUNDLES}}` injected `\n`
+ : x.map(n => ``).join("\n")
+ )
+ .join("\n");
+ };
+
const makeHeaderBundles = helmet => {
const manifest = bundleManifest();
- const manifestLink = manifest ? `` : "";
+ const manifestLink = manifest ? `\n` : "";
const css = bundleCss();
- const cssLink = css && !criticalCSS
- ? ``
- : "";
+ const cssLink = css && !criticalCSS ? `` : "";
const scriptsFromHelmet = ["link", "style", "script", "noscript"]
- .map((tagName) => helmet[tagName].toString()).join("");
+ .map(tagName => helmet[tagName].toString())
+ .join("");
- return `${manifestLink}${cssLink}${scriptsFromHelmet}`;
+ const htmlScripts = htmlifyScripts(groupScripts(routeOptions.unbundledJS.enterHead).scripts);
+ return `${manifestLink}${cssLink}${htmlScripts}\n${scriptsFromHelmet}`;
};
const makeBodyBundles = () => {
const js = bundleJs();
- const css = bundleCss();
- const cssLink = css && criticalCSS ? `` : "";
- const jsLink = js ? `` : "";
+ const jsLink = js ? { src: js } : "";
- return `${cssLink}${jsLink}`;
+ const ins = routeOptions.unbundledJS.preBundle
+ .concat([jsLink])
+ .concat(routeOptions.unbundledJS.postBundle);
+ const htmlScripts = htmlifyScripts(groupScripts(ins).scripts);
+
+ return `${htmlScripts}`;
};
const emptyTitleRegex = /]*><\/title>/;
@@ -221,7 +236,7 @@ function makeRouteHandler(routeOptions, userContent) {
case HEADER_BUNDLE_MARKER:
return makeHeaderBundles(helmet);
case BODY_BUNDLE_MARKER:
- return makeBodyBundles(helmet);
+ return makeBodyBundles();
case PREFETCH_MARKER:
return ``;
case META_TAGS_MARKER:
@@ -257,6 +272,11 @@ const setupOptions = options => {
port: process.env.WEBPACK_DEV_PORT || "2992",
https: Boolean(process.env.WEBPACK_DEV_HTTPS)
},
+ unbundledJS: {
+ enterHead: [],
+ preBundle: [],
+ postBundle: []
+ },
paths: {},
stats: "dist/server/stats.json",
iconStats: "dist/server/iconstats.json",
@@ -269,7 +289,7 @@ const setupOptions = options => {
const chunkSelector = resolveChunkSelector(pluginOptions);
const devProtocol = process.env.WEBPACK_DEV_HTTPS ? "https://" : "http://";
const devBundleBase = `${devProtocol}${pluginOptions.devServer.host}:${pluginOptions.devServer
- .port}/js/`; // eslint-disable-line max-len
+ .port}/js/`;
const statsPath = getStatsPath(pluginOptions.stats, pluginOptions.buildArtifacts);
return Promise.try(() => loadAssetsFromStats(statsPath)).then(assets => {
@@ -287,7 +307,7 @@ const resolveContent = content => {
if (!_.isString(content) && !_.isFunction(content) && content.module) {
const module = content.module.startsWith(".")
? Path.join(process.cwd(), content.module)
- : content.module; // eslint-disable-line
+ : content.module;
return require(module); // eslint-disable-line
}
diff --git a/packages/electrode-react-webapp/package.json b/packages/electrode-react-webapp/package.json
index 6cdbe351d1..e42f4ef635 100644
--- a/packages/electrode-react-webapp/package.json
+++ b/packages/electrode-react-webapp/package.json
@@ -54,5 +54,16 @@
"superagent": "^1.6.1",
"uglify-js": "^2.6.2",
"xclap": "^0.2.0"
+ },
+ "nyc": {
+ "all": true,
+ "check-coverage": true,
+ "statements": 94.02,
+ "branches": 76.11,
+ "functions": 97.96,
+ "lines": 93.82,
+ "cache": true,
+ "reporter": ["lcov", "text", "text-summary"],
+ "exclude": ["coverage", "*clap.js", "gulpfile.js", "dist", "test", "lib/express", "lib/koa"]
}
}
diff --git a/packages/electrode-react-webapp/test/spec/group-scripts.spec.js b/packages/electrode-react-webapp/test/spec/group-scripts.spec.js
new file mode 100644
index 0000000000..9ce7cb3c77
--- /dev/null
+++ b/packages/electrode-react-webapp/test/spec/group-scripts.spec.js
@@ -0,0 +1,68 @@
+"use strict";
+
+const data = [
+ "a",
+ "b",
+ "c",
+ { src: 1 },
+ { src: 5, defer: 1 },
+ { src: 2 },
+ "d",
+ "e",
+ "f",
+ "g",
+ { src: 3 },
+ "h",
+ "i",
+ "j",
+ {
+ src: 4,
+ async: 1
+ },
+ { src: 10 },
+ "k",
+ "l",
+ "m"
+];
+
+const groupScripts = require("../../lib/group-scripts");
+const chai = require("chai");
+
+describe("group-scripts", function() {
+ it("should group scripts", () => {
+ const expected = [
+ "a;\n\nb;\n\nc;",
+ [
+ {
+ src: 1
+ },
+ {
+ src: 5,
+ defer: 1
+ },
+ {
+ src: 2
+ }
+ ],
+ "d;\n\ne;\n\nf;\n\ng;",
+ [
+ {
+ src: 3
+ }
+ ],
+ "h;\n\ni;\n\nj;",
+ [
+ {
+ src: 4,
+ async: 1
+ },
+ {
+ src: 10
+ }
+ ],
+ "k;\n\nl;\n\nm;"
+ ];
+ const result = groupScripts(data);
+ chai.expect(result.scripts).to.deep.equal(expected);
+ });
+});
diff --git a/packages/electrode-react-webapp/test/spec/index.spec.js b/packages/electrode-react-webapp/test/spec/index.spec.js
index 0a187034a2..a42633df0d 100644
--- a/packages/electrode-react-webapp/test/spec/index.spec.js
+++ b/packages/electrode-react-webapp/test/spec/index.spec.js
@@ -184,10 +184,10 @@ describe("Test electrode-react-webapp", () => {
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain(
- '