Search code examples
javascriptnode.jscommonjses6-modulestemplate-literals

Why cannot modify a value of an object in a CommonJS module?


I have this basic koa v2 app in which I try to implement template literals as a viewing engine. In ./views/index.js how can I populate the value of state from app.js?

My problem is that I want to push the object values from ctx.body = index.render({}) all the way down to .views/partials/mainTop.js file, where let's say I want to include title object value between the <title></title> tags. Is there a way to achieve this without requiring .views/partials/mainTop.js in app.js? I want to achieve similar thing with template literals if it were handlebars or nunjucks templates.

./app.js

const index = require('./views/index');
const app = new Koa();

app.use(ctx => {
  index.state = {
    foo: 'bar',
  };
  ctx.body = index.render({
    title: 'Template Literals',
    description: 'Vanilla JS rendering',
  });
});

app.listen(3000);

./views/index.js

const main = require('./layouts/main');

let state = {};
module.exports.state = state;
console.log(state); // returning an {} empty object, expected => { foo: "bar" }

module.exports.render = (obj) => {
  return main.render(`
    <p>Hello world! This is HTML5 Boilerplate.</p>
    ${JSON.stringify(obj, null, 4)}}
    ${obj.title}
  `);
};

./views/layouts/main.js

const mainTop = require('../partials/mainTop');
const mainBottom = require('../partials/mainBottom');

module.exports.render = (content) => {
  return `
    ${mainTop.render}

    ${content}

    ${mainBottom()}
  `;
}

./views/partials/mainTop.js

const render = `
  <!doctype html>
  <html class="no-js" lang="">
  <head>
  <title></title>
  ...
`;
module.exports.render = render;

./views/partials/mainBottom.js

module.exports = () => {
  return `
    ...
    </body>
    </html>
  `;
}

Solution

  • console.log(state); // returning an {} empty object, expected => { foo: "bar" }

    Of course you're getting the empty object you just created:

    • it's the module.exports.state property that is getting assigned to in app.js. Your local state variable is not overwritten, nor is the object it refers to mutated. To do that, you'd need to do index.state.foo = "bar"; in index.js.
    • the object/variable is logged immediately after it was created as an empty object. The assignment in index.js happens asynchronously, inside an app.use callback. If you did the console.log inside the render method, which is called after the assignment, you'd get the value as expected. However in that case you probably should just pass it as a parameter.