Coding and Debugging with the SEO Page Generator

March 6, 2018: Note that this information was correct at the date of publish (August 10, 2016) but some has changed. In general it is still accurate, but some information (such as the outputs screenshot) applies to the old page generator (EnvJS and V8) and not the newer one (Prerender). If anything doesn't seem right, refer to the newer blog post and — as always — the documentation.

The SEO page generator exists because of a challenge faced by SuiteCommerce Advanced. As SCA uses the single page application model, content is generated and displayed dynamically and on demand, and is constructed using JavaScript. As search engines typically ignore JavaScript, an alternative for is required so that the content can be indexed.

In this article, I want to run through some things about the SEO page generator that you may not know, as well as go over some tips for debugging and getting the most out of it.

Hiding from the Spiders

Search engines work by 'spidering' sites: finding every link on a page and clicking them, finding every link on those pages and clicking them, etc, until the whole site has been covered and a web has formed.

It is usually a good thing to have the search bots do this: it means your pages will now show up in their results. However, there are some things that you will want to exclude for one or more of the following example reasons:

  1. It contains sensitive or personal information
  2. It could affect performance
  3. It is unnecessary and could negatively affect your search engine listings

It's on the final point that I want to offer examples, as it'll help illustrate one of the best tools available to you.

Show Only to Shoppers

On search results pages, shoppers can hover over a product item and click a button that creates a 'quick view' dialog; this lets the shopper quickly look at a summary of the product and add it to their cart should they wish (ie without leaving the search results page).

Note the class we put on it: facets-item-cell-grid-quick-view-wrapper. Now, if you inspect the element in your browser's developer tools, you'll see it in the DOM. You can manipulate it and do stuff with it: it's definitely there. Now, view the page's source. Search for the class and you won't find it. What's going on?

This is deliberate. We don't want search engines accessing the quick view dialog: it's not a 'page', it's not something that you can point a user directly to. Furthermore, the dialog requires JavaScript, so search engines couldn't use it anyway.

What we want the search engine to do is to click the link that takes them to the full product detail page, which is an actual page and contains a full list of details. How do we do this?

The answer lies in two places: Facets.ItemCell.View.js and facets_item_cell_grid.tpl. Firstly, in the context object of the view is a property called isEnvironmentBrowser which is determined by the following code:

,   isEnvironmentBrowser: SC.ENVIRONMENT.jsEnvironment === 'browser' && !SC.ENVIRONMENT.isTouchEnabled

It's the first part that's important. If you were at SuiteWorld 2016, you may have seen a presentation by Bruce Tanenholtz; in it, he talked about troubleshooting SEO page generation and, in particular, jsEnvironment.

jsEnvironment can be one of two possible values: browser or server. They literally refer to when frontend JavaScript is being executed in the browser (ie the visitor is a human) or on the NetSuite servers (ie the visitor is a bot and the SEO page generator is being used). Thus, what you can do is set up your templates or services so that certain aspects only show/run when it is being executed for a human user or a search bot.

So in the above example (to do with individual search items) we have added code to check whether the environment is in the browser. If it is, then the following code runs:

{{#if isEnvironmentBrowser}}
<div class="facets-item-cell-grid-quick-view-wrapper">
    <a href="{{url}}" class="facets-item-cell-grid-quick-view-link" data-toggle="show-in-modal">
        <i class="facets-item-cell-grid-quick-view-icon"></i>
        {{translate 'Quick View'}}

Or, in other words, generate the quick view button if the visitor is using a browser and they are not on a mobile device.

Show Only to Bots

For an inverse use of this — ie, where we want to only execute code on the server — then you can take a look at CMSadapter.js. In it, we have the following code:

if (SC.ENVIRONMENT.jsEnvironment === 'server')

In the scope of things, we would set the title of the page always. However, there is an issue with Internet Explorer that makes this a bad idea to do browser side. Thus, rather than not bother, we can still choose to set it when a search bot visits.

Behind the Scenes

For the curious, you can find jsEnvironment being defined in ShoppingApplication > SuiteScript > shopping.ssp:

var SC = window.SC = {
    jsEnvironment: (typeof nsglobal === 'undefined') ? 'browser' : 'server'

If nsglobal (a global variable) is undefined then set it to browser, if it is defined then it's server. nsglobal is a variable injected into the global namespace when the SEO page generator is being used. When the application discovers that the visitor is a search bot (ie they don't have JavaScript enabled) the application runs all the frontend JavaScript on the backend instead. Once the frontend JavaScript has run, the outputted HTML is then sent to the visitor. Thus, if the frontend JavaScript is running in the browser, nsglobal will not be used.

Debugging with the SEO Page Generator

If you haven't realised already, this means that you can use the SEO page generator to debug your site. It's also important to test how your site behaves when a search bot hits it. To test this, open up your browser and disable JavaScript, then reload the page.

A good place to see this in action is the search results page. After all, I've just talked about how quick view links are hidden to search bots, so now you can see it in action. After disabling JavaScript and reloading the search results page, note how you can't see any of the links to launch quick view.

A more specific thing you can do to debug is the use the ?seodebug=T parameter at the end of your site's URL while JavaScript is disabled. View the page source and scroll to the bottom of the page, you should see a whole lot of HTML comments — these are logs from the server.

Sometimes, a cached version of the page (or its resources) will be served instead. In these cases, an old version of the page is being served. To work around this, attach a unique junk parameter to the end of the URL. So, for example, ?seodebug=T&thisis=junk — as long as it hasn't been used before, the app will generate a new, fresh page. Every time you want to test something (even if you refresh) you'll need to attach a new, unique value to the end.

With this source code, you can then do a search for the word "error" to see if anything comes up. Let's take a look at some common errors you might see.

Script Execution Time Exceeded

A site is only given 30 seconds to build the page based on the scripts provided to it. If, after 30 seconds, it is not built then an error is served.

If you see this then you need to debug your JavaScript. Either you've written some very inefficient code that's taking a long time to process, or it's got stuck: either waiting for something that won't happen or it's in a loop.

Script Out of Memory

There is a limit to how much memory can be dedicated to generating a page; if you exceed it, it will cause an error. Note that this limit includes objects created by JavaScript: thus you may look at your files and think it's a reasonable amount, but the code you write may end up creating things that causes this limit to be hit.

Again, you need to debug your JavaScript. If there's no specific thing that points to a large object, you'll need to look for things that operate loops, and see if you've accidentally coded a loop that doesn't break.

TypeError: Object #<Object> has no method 'close'

This is a red herring. It only appears in the reference app. You don't need to worry about it.

Returning Your Own Information

When developing, you may have used console.log in some of your frontend code, perhaps to return some values or test where your code gets stuck. You can then use the developer console in your browser to see the logs. Using the above method, you can also do the same with your backend code.

In order to do this, simply open up a service or backend configuration file, put your console.log in, and upload it to the server. Then just repeat the steps to get to debug mode. For example, in my SC.Shopping.Configuration.js file I've put console.log('Hello world!');. Guess what appears in my page's source?

Thus, if you need to return some information, object or variable then you can use this way. Fun fact: if you then run this locally, the console.log will appear in the browser console.


The SEO page generator is a necessary part of the application. It effectively operates like a headless browser, processing the frontend JavaScript and then outputting the generated web page. It is this generated content that is sent to the search engines.

We can use this same engine to debug the site. By disabling JavaScript and passing a URL parameter, we can trigger the generator to run and for debug mode to be on. From here, we can see if any errors are being generated. Alternatively, we can also use this to log messages to help in the development of backend code.

Further Reading