Skip to content

Commit

Permalink
[minor] refresh content module for webpack dev mode (#835)
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip authored Jun 28, 2018
1 parent 0fd759d commit 3a7e825
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 56 deletions.
9 changes: 5 additions & 4 deletions packages/electrode-react-webapp/lib/express/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const registerRoutes = (app, options, next = () => {}) => {
);
return ReactWebapp.resolveContent(v.content);
}
return { status: 200, html: "" };
return { content: { status: 200, html: "" } };
};

const routeOptions = _.defaults({ htmlFile: v.htmlFile }, registerOptions);
Expand All @@ -58,9 +58,10 @@ const registerRoutes = (app, options, next = () => {}) => {
if (method === "*") {
method = "ALL";
}
app[method.toLowerCase()](path, (req, res) =>
handleRoute(req, res, routeHandler, content || (content = resolveContent()))
);
app[method.toLowerCase()](path, (req, res) => {
if (!content) content = resolveContent();
handleRoute(req, res, routeHandler, content.content);
});
});
});

Expand Down
23 changes: 20 additions & 3 deletions packages/electrode-react-webapp/lib/hapi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const registerRoutes = (server, options) => {
);
return ReactWebapp.resolveContent(pathData.content);
}
return { status: 200, html: "" };
return { content: { status: 200, html: "" } };
};

const routeOptions = ReactWebapp.setupPathOptions(registerOptions, path);
Expand All @@ -57,8 +57,25 @@ const registerRoutes = (server, options) => {
method: pathData.method || "GET",
path,
config: pathData.config || {},
handler: (req, reply) =>
handleRoute(req, reply, routeHandler, content || (content = resolveContent()))
handler: (req, reply) => {
if (req.app.webpackDev) {
const wpd = req.app.webpackDev;
if (!wpd.valid) {
content = ReactWebapp.resolveContent("<!-- Webpack still compiling -->");
} else if (wpd.hasErrors) {
content = ReactWebapp.resolveContent("<!-- Webpack compile has errors -->");
} else if (!content || content.resolveTime < wpd.compileTime) {
if (content && content.fullPath) {
delete content.xrequire.cache[content.fullPath];
}
content = resolveContent();
}
} else if (!content) {
content = resolveContent();
}

handleRoute(req, reply, routeHandler, content.content);
}
});
});
};
Expand Down
5 changes: 3 additions & 2 deletions packages/electrode-react-webapp/lib/koa/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const registerRoutes = (router, options) => {
);
return ReactWebapp.resolveContent(v.content);
}
return { status: 200, html: "" };
return { content: { status: 200, html: "" } };
};

const routeOptions = _.defaults({ htmlFile: v.htmlFile }, registerOptions);
Expand All @@ -69,7 +69,8 @@ const registerRoutes = (router, options) => {

/*eslint max-nested-callbacks: [0, 4]*/
router(method.toLowerCase(), path, function() {
return handleRoute.call(this, routeHandler, content || (content = resolveContent()));
if (!content) content = resolveContent();
return handleRoute.call(this, routeHandler, content.content);
}); //end get
});
});
Expand Down
34 changes: 25 additions & 9 deletions packages/electrode-react-webapp/lib/react-webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,37 @@ const setupPathOptions = (routeOptions, path) => {
};

const resolveContent = (content, xrequire) => {
const resolveTime = Date.now();

if (!_.isString(content) && !_.isFunction(content) && content.module) {
const module = content.module.startsWith(".")
? Path.join(process.cwd(), content.module)
: content.module;
const mod = content.module.startsWith(".") ? Path.resolve(content.module) : content.module;

xrequire = xrequire || require;

try {
return xrequire(module);
} catch (err) {
const msg = `electrode-react-webapp: load SSR content ${module} failed`;
console.error(msg, err); // eslint-disable-line
return msg;
return {
fullPath: xrequire.resolve(mod),
xrequire,
resolveTime,
content: xrequire(mod)
};
} catch (error) {
const msg = `electrode-react-webapp: load SSR content ${mod} failed - ${error.message}`;
console.error(msg, "\n", error); // eslint-disable-line
return {
fullPath: null,
error,
resolveTime,
content: msg
};
}
}

return content;
return {
fullPath: null,
resolveTime,
content
};
};

module.exports = {
Expand Down
5 changes: 5 additions & 0 deletions packages/electrode-react-webapp/test/data/test-content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
"status": 200,
"html": "<div>Test1 Electrode</div>",
"prefetch": "console.log('test1');"
};
30 changes: 18 additions & 12 deletions packages/electrode-react-webapp/test/spec/express.index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,25 @@ describe("express electrode-react-webapp", function() {

it("should render to static markup", () => {
const server = startServer(webappOptions());
return new Promise((resolve, reject) => {
const port = server.address().port;
return request(`http://localhost:${port}`).end((err, resp) => {
if (err) return reject(err);
try {
expect(resp.text).includes("<div>Hello Electrode</div>");
expect(resp.text).includes("console.log('Hello');");
} catch (err2) {
reject(err2);
}
return server.close(() => resolve());
const makeRequest = () => {
return new Promise((resolve, reject) => {
const port = server.address().port;
return request(`http://localhost:${port}`).end((err, resp) => {
if (err) return reject(err);
try {
expect(resp.text).includes("<div>Hello Electrode</div>");
expect(resp.text).includes("console.log('Hello');");
} catch (err2) {
return reject(err2);
}
return resolve();
});
});
});
};

return makeRequest()
.then(() => makeRequest())
.then(() => new Promise(resolve => server.close(resolve)));
});

it("should render to static markup @func_content", () => {
Expand Down
196 changes: 182 additions & 14 deletions packages/electrode-react-webapp/test/spec/hapi.index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

/* eslint-disable quotes */

const Fs = require("fs");
const Promise = require("bluebird");
const assign = require("object-assign");
const electrodeServer = require("electrode-server");
Expand Down Expand Up @@ -106,19 +107,24 @@ describe("hapi electrode-react-webapp", () => {

it("should successfully render to static markup", () => {
return electrodeServer(config).then(server => {
return server
.inject({
method: "GET",
url: "/"
})
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<div>Hello Electrode</div>");
expect(res.result).to.contain("<script>console.log('Hello');</script>");
expect(res.result).to.not.contain("Unknown marker");
stopServer(server);
})
const makeRequest = () => {
return server
.inject({
method: "GET",
url: "/"
})
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<div>Hello Electrode</div>");
expect(res.result).to.contain("<script>console.log('Hello');</script>");
expect(res.result).to.not.contain("Unknown marker");
});
};

return makeRequest()
.then(() => makeRequest())
.then(() => stopServer(server))
.catch(err => {
stopServer(server);
throw err;
Expand Down Expand Up @@ -762,7 +768,7 @@ describe("hapi electrode-react-webapp", () => {
configOptions.prodBundleBase = "http://awesome-cdn.com/myapp/";
configOptions.stats = "test/data/stats-test-one-bundle.json";
configOptions.htmlFile = "test/data/index-1.html";
configOptions.paths["/{args*}"].htmlFile = Path.resolve("test/data/index-2.html");
paths.htmlFile = Path.resolve("test/data/index-2.html");

return electrodeServer(config).then(server => {
return server
Expand Down Expand Up @@ -1216,4 +1222,166 @@ describe("hapi electrode-react-webapp", () => {
});
});
});

describe("with webpackDev", function() {
it("should skip if webpack dev is not valid", () => {
return electrodeServer(config).then(server => {
server.ext({
type: "onRequest",
method: (request, reply) => {
request.app.webpackDev = { valid: false };
reply.continue();
}
});
const makeRequest = () => {
return server.inject({ method: "GET", url: "/" }).then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<!-- Webpack still compiling -->");
});
};

return makeRequest()
.then(() => stopServer(server))
.catch(err => {
stopServer(server);
throw err;
});
});
});

it("should skip if webpack compile has errors", () => {
return electrodeServer(config).then(server => {
server.ext({
type: "onRequest",
method: (request, reply) => {
request.app.webpackDev = { valid: true, hasErrors: true };
reply.continue();
}
});
const makeRequest = () => {
return server.inject({ method: "GET", url: "/" }).then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<!-- Webpack compile has errors -->");
});
};

return makeRequest()
.then(() => stopServer(server))
.catch(err => {
stopServer(server);
throw err;
});
});
});

it("should successfully render to static markup", () => {
return electrodeServer(config).then(server => {
const compileTime = Date.now();
server.ext({
type: "onRequest",
method: (request, reply) => {
request.app.webpackDev = { valid: true, hasErrors: false, compileTime };
reply.continue();
}
});
const makeRequest = () => {
return server
.inject({
method: "GET",
url: "/"
})
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<div>Hello Electrode</div>");
expect(res.result).to.contain("<script>console.log('Hello');</script>");
expect(res.result).to.not.contain("Unknown marker");
});
};

return makeRequest()
.then(() => makeRequest())
.then(() => stopServer(server))
.catch(err => {
stopServer(server);
throw err;
});
});
});

it("should refresh content module", () => {
paths.content = {
module: "./test/data/test-content"
};

return electrodeServer(config).then(server => {
let compileTime = Date.now();

const testContent1 = {
status: 200,
html: "<div>Test1 Electrode</div>",
prefetch: "console.log('test1');"
};

const testContent2 = {
status: 200,
html: "<div>Test2 Electrode</div>",
prefetch: "console.log('test2');"
};

server.ext({
type: "onRequest",
method: (request, reply) => {
request.app.webpackDev = { valid: true, hasErrors: false, compileTime };
reply.continue();
}
});
const makeRequest = () => {
return server.inject({
method: "GET",
url: "/"
});
};

const updateTestContent = obj => {
Fs.writeFileSync(
Path.resolve("test/data/test-content.js"),
`module.exports = ${JSON.stringify(obj, null, 2)};\n`
);
};

updateTestContent(testContent1);

return Promise.try(makeRequest)
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<div>Test1 Electrode</div>");
expect(res.result).to.contain("<script>console.log('test1');</script>");
expect(res.result).to.not.contain("Unknown marker");
updateTestContent(testContent2);
compileTime = Date.now();
})
.then(() => makeRequest())
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain("<title>Electrode App</title>");
expect(res.result).to.contain("<div>Test2 Electrode</div>");
expect(res.result).to.contain("<script>console.log('test2');</script>");
expect(res.result).to.not.contain("Unknown marker");
updateTestContent(testContent2);
})
.then(() => stopServer(server))
.catch(err => {
stopServer(server);
throw err;
})
.finally(() => {
updateTestContent(testContent1);
});
});
});
});
});
Loading

0 comments on commit 3a7e825

Please sign in to comment.