npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

doppelganger

v0.1.2

Published

Run Backbone.js apps on a Node.js server

Downloads

11

Readme

Doppelganger

Run Backbone.js apps on a Node.js server

The motivation behind Doppelganger

Single-page apps are great for interactivity, but they typically face problems with SEO, accessibility and bookmarking, due to the fact that their pages do not exist anywhere as externally accessible resources.

Doppelganger solves these problems by running an instance of the exact same single-page app on the server using Node.js. This server-side app instance is used to generate the HTML that is presented to a web crawler or a user who has JavaScript disabled.

This means that that the HTML that is dynamically generated on the server-side exactly mirrors the DOM structure created by the client side code. All the URL routes within the app are faithfully duplicated, and the content at each URL is identical to the content that is generated on the client-side when the user navigates to that URL route within the app.

The best thing about this is that with Doppelganger, you get all these benefits without needing to perform any modifications whatsoever to the client-side code, so you can be up and running within minutes.

How Doppelganger works

Doppelganger takes a Backbone.js app, and instantiates it 'behind the scenes' on the Node.js server. This gives the server a live copy of the client-side app to work with.

When a request is made to the server, the following steps are typically carried out:

  1. The server checks through the routes that are registered within the Backbone app instance
  2. If it finds a route that matches the requested URL, it calls the app instance's Backbone.history.navigate() method (or if no matching routes are found, the server responds appropriately)
  3. This causes the server-side app instance to update its DOM hierarchy accordingly
  4. The server gets an HTML version of the app instance's updated DOM hierarchy
  5. This HTML is sent back as the response body

These steps mean that with minimal effort, the single-page app will now be fully accessible without relying on any client-side scripting.

Examples

The project at https://github.com/timkendrick/doppelganger-examples provides a demonstration of Doppelganger in action.

View the source of pages generated by the Doppelganger server to see the HTML that has been dynamically inserted on the server-side.

Installation

# Install the latest version of Doppelganger using NPM
npm install doppelganger

Usage

Instantiating a Doppelganger app

The main Doppelganger class is packaged as a CommonJS module, so once it is installed you can use require() to use it in another module:

var Doppelganger = require('doppelganger');

// Instantiate the app, passing in the base HTML and the path to the Require.js config file
var appInstance = new Doppelganger(fs.readFileSync('index.html', 'utf8'), 'js/config.js');

// Initialise the app instance, passing a callback that is invoked when initialisation is complete 
appInstance.init(function() { console.log('App initialised'); });

Checking whether a route exists within a Doppelganger app instance

var routeExists = appInstance.routeExists(route);

Navigating to a route within a Doppelganger app instance

appInstance.navigate(route);

Getting a Doppelganger app instance's current DOM state as HTML

var html = appInstance.getHTML();

Running multiple Doppelganger app instances simultaneously

var mainAppInstance = new Doppelganger(fs.readFileSync('main/index.html', 'utf8'), 'main/js/config.js', 'main');
var faqAppInstance = new Doppelganger(fs.readFileSync('faq/index.html', 'utf8'), 'faq/js/config.js', 'faq')
var cmsAppInstance = new Doppelganger(fs.readFileSync('cms/index.html', 'utf8'), 'cms/js/config.js', 'cms')

// Important: ensure that only one `init()` method is called at a time!
mainAppInstance.init(function() {
	console.log("Main app initialised");
	
	faqAppInstance.init(function() {
		console.log("FAQ app initialised");
		
		cmsAppInstance.init(function() {
			console.log("CMS app initialised");
		});
	});
});

Caveats to bear in mind when using Doppelganger

  • The Backbone.js app must use Require.js to load its dependencies
  • The Doppelganger constructor expects to be passed a path to a config JavaScript file that includes a sole require.config() call, in order to initialise Require.js correctly
  • Doppelganger expects Require.js paths to be set for "jquery" and "backbone" in the Require.js config file
  • The app code will not have access to any global variables:
    • Global browser variables such as window, document, and history will not be set. Avoid writing code that depends on the browser environment (although you do have access to jQuery for DOM manipulation, see below)
    • The $, _ and Backbone global variables will not be set. These should instead be accessed using Require.js (e.g. by listing "jquery" amongst a module's dependencies - this is good practice anyway).
  • When running multiple app instances simultaneously, each instance needs its own Require.js context. Be aware of the implications of using Require.js in multiversion mode.
  • When running multiple app instances simultaneously, only one instance's init() function can be called at a time
  • JSDOM is used as the server-side DOM library