2

I've built few pages of a static website using ExpressJS and PUG to get the advantage of the template engine.

But now I need to export all the raw HTML that is being rendered by all ExpressJS Routes.

Is there any package that can help me to do that? Or I've to write custom command and iterate over all the Routes and save the rendered output?

If a custom command is the only way, how do I iterate over all the routes and get the rendered output?

Robin
  • 446
  • 2
  • 4
  • 24
  • Before answering your question, can you explain _why_ you think you need to do what you describe in your second sentence? Because this feels like it might actually be an [XY question](https://meta.stackexchange.com/a/66378/395686). After all, if your site builds what is effectively static HTML content, why wouldn't you use a static site generator instead? – Mike 'Pomax' Kamermans Mar 02 '20 at 18:19
  • 1
    To access rendered html before rendering a view you can use the pug compileFile method. https://pugjs.org/api/reference.html#pugrenderfilepath-options-callback You can then write the render to a file using Node's writefile. https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options – Zenkylo Mar 02 '20 at 19:27

2 Answers2

2

I couldn't find any library or resource to achieve what I wanted. But with some of my dirty code, hacks, and packages I was able to export all the routes.

Note: Instead of writing a node command to export the htmls, I've added a route to trigger the operations here is the code for the route:

app.use('/export_templates', router.get('/', async function (req, res, next) {
  const endpoints = listEndpoints(app);
  const failedEndpoints = [];

  for (const i in endpoints) {
    const endpoint = endpoints[i];

    if (endpoint.path == '/export_templates') {
      continue;
    }

    try {
      const res = await axios.get('http://'+req.headers.host+''+endpoint.path+'?export=true');
    }
    catch(error) {
      failedEndpoints.push(endpoint.path);
    }
  }

  res.json({
    "status": "succes",
    "message": "Please check templates folder for the latest exported html templates",
    "failed": failedEndpoints
  })
}));

Basically this route iterates and makes a request to all the available routes with a export=true parameter.

Then inside every route view function a condition checks if the export parameter is available then calls the exportTemplateFile function with the pug template location and new file name as the function parameter. If the request doesn't contain export parameter the requested route will simply output what template.

An example route:

router.get('/', function(req, res, next) {
  if (req.query.export) {
    exportTemplateFile('views/index.pug', 'index.html');
  }

  res.render('index.pug');
});

And here is the code for 2 util function to complete the export process

function createTemplateFile(filename) {
  fs.open(filename,'r',function(err, fd){
    if (err) {
      fs.writeFile(filename, '', function(err) {
          if(err) {
              console.log(err);
          }
      });
    }
  });
}

function exportTemplateFile(templateLocation, templateName) {
  const html = pretty(pug.renderFile(templateLocation));

  createTemplateFile('templates/'+templateName);

  var stream = fs.createWriteStream('templates/'+templateName);
  stream.once('open', function (fd) {
    stream.write(html);
    stream.end();
  });
}

The createTemplateFile function simply creates a new file if it doesn't exist.

The exportTemplateFile function saves the HTML in the html variable rendered by pug and prettifies it with the pretty package and then overwrites the new template file.

Note: In my case all the pug templates were static so I didn't have to pass any context to the pug.renderFile function. But if you need any context to be used inside the pug template you can simply pass that with the template location.

Robin
  • 446
  • 2
  • 4
  • 24
0

Edited version of the same answer.

First of all thank you so much for solving this problem. I have made some changes to your code as per new errors.

Here is the code with async and await function for ejs users

const express = require('express')
const ejs = require('ejs')
const fs = require('fs')
const app = express()
const port = 3000

//set the templating engine as ejs
app.set('view engine', 'ejs');

function createTemplateFile(filename) {
    fs.open(filename,'r',function(err, fd){
      if (err) {
        fs.writeFile(filename, '', function(err) {
            if(err) {
                console.log(err);
            }
        });
      }
    });
  }
  
  async function exportTemplateFile(templateLocation, templateName) {
    var html = await ejs.renderFile(templateLocation);
  
    createTemplateFile('templates/'+templateName);
  
    var stream = fs.createWriteStream('templates/'+templateName);
    stream.once('open', function (fd) {
      stream.write(`${html}`);
      stream.end();
    });
  }

app.get('/', (req, res, next) => {
  res.render('./pages/home')
    exportTemplateFile('views/pages/home.ejs', 'index.html');
    console.log('file rendered and saved successfully')
})

app.listen(port, () => {
  console.log(`App is listening on port ${port}`)
})
xdayaan
  • 57
  • 7