Skip to content

Commit

Permalink
support insert token ids to output for debugging (#902)
Browse files Browse the repository at this point in the history
* support insert token ids to output for debugging

* update design doc link in README
  • Loading branch information
jchip authored Aug 8, 2018
1 parent b723108 commit 0be1eda
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/electrode-react-webapp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This module helps render and serve your Electrode React application's `index.htm

All the defaults are configured out of the box, but your index page is extensible. You can specify your own index template file with the `htmlFile` option.

See [design](./DESIGN.md) for details on how the template can be extended.
See [design](/packages/electrode-react-webapp/DESIGN.md) for details on how the template can be extended.

## Installing

Expand Down
1 change: 1 addition & 0 deletions packages/electrode-react-webapp/lib/async-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class AsyncTemplate {
this._initializeTokenHandlers(this._tokenHandlers);
this._applyTokenLoad();
this._renderer = new Renderer({
insertTokenIds: this._options.insertTokenIds,
htmlTokens: this._tokens,
tokenHandlers: this._tokenHandlers
});
Expand Down
1 change: 1 addition & 0 deletions packages/electrode-react-webapp/lib/react-webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function makeRouteHandler(routeOptions) {
const asyncTemplate = new AsyncTemplate({
htmlFile: routeOptions.htmlFile,
tokenHandlers: tokenHandlers.filter(x => x),
insertTokenIds: routeOptions.insertTokenIds,
routeOptions
});

Expand Down
20 changes: 17 additions & 3 deletions packages/electrode-react-webapp/lib/render-execute.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

/* eslint-disable complexity */
const Promise = require("bluebird");

const { TOKEN_HANDLER } = require("./symbols");
Expand All @@ -20,25 +21,38 @@ function renderNext(err, xt) {
context.handleError(err);
}

const insertTokenId = tk => {
context.output.add(`<!-- ${tk.id} -->\n`);
};

const insertTokenIdEnd = tk => {
context.output.add(`<!-- $~${tk.id}~$ -->\n`);
};

if (context.isFullStop || context.isVoidStop || xt.stepIndex >= renderSteps.length) {
return xt.resolve(context.output.close());
} else {
// TODO: support soft stop
const step = renderSteps[xt.stepIndex++];
const tk = step.tk;
const withId = step.insertTokenId;
switch (step.code) {
case STEP_HANDLER:
return context.handleTokenResult(tk.id, tk[TOKEN_HANDLER](context, tk), e =>
renderNext(e, xt)
);
if (withId) insertTokenId(tk);
return context.handleTokenResult(tk.id, tk[TOKEN_HANDLER](context, tk), e => {
if (withId) insertTokenIdEnd(tk);
renderNext(e, xt);
});
case STEP_STR_TOKEN:
context.output.add(tk.str);
break;
case STEP_NO_HANDLER:
context.output.add(`<!-- unhandled token ${tk.id} -->`);
break;
case STEP_LITERAL_HANDLER:
if (withId) insertTokenId(tk);
context.output.add(step.data);
if (withId) insertTokenIdEnd(tk);
break;
}
return renderNext(null, xt);
Expand Down
32 changes: 28 additions & 4 deletions packages/electrode-react-webapp/lib/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,19 @@ const { STEP_HANDLER, STEP_STR_TOKEN, STEP_NO_HANDLER, STEP_LITERAL_HANDLER } =

class Renderer {
constructor(options) {
const insertTokenIds = Boolean(options.insertTokenIds);
// the last handler wins if it contains a token
const tokenHandlers = options.tokenHandlers.reverse();

const makeNullRemovedStep = (tk, cause) => {
return {
tk,
insertTokenId: false,
code: STEP_LITERAL_HANDLER,
data: `<!-- ${tk.id} removed due to its ${cause} -->\n`
};
};

const makeHandlerStep = tk => {
// look for first handler that has a token function for tk.id
const handler = tokenHandlers.find(h => h.tokens.hasOwnProperty(tk.id));
Expand All @@ -23,17 +33,24 @@ class Renderer {
const tkFunc = handler.tokens[tk.id];

if (tkFunc === null) {
if (insertTokenIds) return makeNullRemovedStep(tk, "handler set to null");

return null;
}

if (typeof tkFunc !== "function") {
// not a function, just add it to output
return { tk, code: STEP_LITERAL_HANDLER, data: tkFunc };
return {
tk,
code: STEP_LITERAL_HANDLER,
insertTokenId: insertTokenIds && !tk.props._noInsertId,
data: tkFunc
};
}

tk.setHandler(tkFunc);

return { tk, code: STEP_HANDLER };
return { tk, code: STEP_HANDLER, insertTokenId: insertTokenIds && !tk.props._noInsertId };
};

const makeStep = tk => {
Expand All @@ -45,9 +62,16 @@ class Renderer {
// token is not pointing to a module, so lookup from token handlers
if (!tk.isModule) return makeHandlerStep(tk);

if (tk.custom === null) return null;
if (tk.custom === null) {
if (insertTokenIds) return makeNullRemovedStep(tk, "process return null");
return null;
}

return { tk, code: STEP_HANDLER };
return {
tk,
code: STEP_HANDLER,
insertTokenId: options.insertTokenIds && !tk.props._noInsertId
};
};

this.renderSteps = options.htmlTokens.map(makeStep).filter(x => x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function setup() {
return {
name: "custom-call",
process: function() {
return `_call`;
return `_call process from custom-call token fixture`;
}
};
}
Expand Down
54 changes: 54 additions & 0 deletions packages/electrode-react-webapp/test/spec/hapi.index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,60 @@ describe("hapi electrode-react-webapp", () => {
});
});

it("should successfully render to static markup with insert IDs", () => {
configOptions.insertTokenIds = true;
configOptions.templateProcessor = asyncTemplate => {
asyncTemplate.addTokens({
insert: "before",
id: "PAGE_TITLE",
tokens: [
{ token: "#./test/fixtures/custom-null" },
{ token: "#./test/fixtures/custom-1" },
{ token: "#./test/fixtures/custom-call", props: { _call: "setup", _noInsertId: true } }
]
});
};
return electrodeServer(config).then(server => {
const makeRequest = () => {
return server
.inject({
method: "GET",
url: "/"
})
.then(res => {
expect(res.statusCode).to.equal(200);
const result = res.result;
expect(result).to.contain("<title>Electrode App</title>");
expect(result).to.contain("<div>Hello Electrode</div>");
expect(result).to.contain("<script>console.log('Hello');</script>");
expect(result).to.not.contain("Unknown marker");
expect(result).contains("<!-- INITIALIZE -->");
expect(result).contains("<!-- $~INITIALIZE~$ -->");
expect(result).contains("<!-- PAGE_TITLE -->");
expect(result).contains("<!-- $~PAGE_TITLE~$ -->");
expect(result).contains("<!-- #./test/fixtures/custom-1 -->");
expect(result).contains("<!-- $~#./test/fixtures/custom-1~$ -->");
expect(result).contains("_call process from custom-call token fixture");
expect(result).not.contains("<!-- #./test/fixtures/custom-call -->");
expect(result).contains(
"<!-- HEAD_INITIALIZE removed due to its handler set to null -->"
);
expect(result).contains(
"<!-- #./test/fixtures/custom-null removed due to its process return null -->"
);
});
};

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

it("should successfully render to static markup with content as a function", () => {
assign(mainRoutePathOptions, {
content: () =>
Expand Down
2 changes: 1 addition & 1 deletion packages/electrode-react-webapp/test/spec/token.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("token", function() {
expect(tk.id).to.equal("#./test/fixtures/custom-call");
expect(tk.isModule).to.equal(true);
tk.load();
expect(tk[TOKEN_HANDLER]()).to.equal("_call");
expect(tk[TOKEN_HANDLER]()).to.equal("_call process from custom-call token fixture");
});

it("should create token as custom and call setup only once for each token", () => {
Expand Down

0 comments on commit 0be1eda

Please sign in to comment.