pmp-engine

2.3.1 • Public • Published

npm version

pmp-engine

Ever wanted to experiment on and customize a webpage ? The Pimp my page engine let's you do just that. The engine leverage the mighty BrowserSync to allow full HTML, CSS or JS modifications on any webpage.

  • write SASS, it's automatically compiled to CSS and injected in the page (no refresh needed)
  • write JS, the browser's refresh with your custom JS injected
  • write some HTML and jQuery like operations to swap/modify the page content

How it works

The trick is quite simple, some URL (ex: http://www.thetimes.co.uk/) is being proxyfied with BrowserSync, it is then available through something along the lines of localhost:3000. Nothing special, but now we have the opportunity to add some middleware. This middleware is the corner stone of all "pimping" we gonna apply to the Times website pages.

Basically what it does is detect all HTML responses matching with a whitelist of URL patterns (ex:"*/magazine/*"). When one such URL request is identified for pimping, the middleware will change the response according to a set of jQuery instructions: add some HTML there, remove this style tag, append a new stylesheet...

This core behavior is then bundled with some gulp automated build tasks goodness to provide external page assets (e.g. stylesheets, js, HTML partials, images).

Finally the browser behavior is tied to gulp watch tasks to react according to real-time assets modifications: inject in the page, reload page with new assets or restart the whole thing with new instructions.

To you this mean live pimping of the Times style magazine pages in your local environment. Useful for:

  • trying bug fixing production code
  • quick prototyping features
  • proto-theming without the need for a local environment
  • just for fun of defacing a site in your own sandboxed world (no legal risks 😇)

Install

npm install pmp-engine

Setup

given that you already installed the dependency, here is how to get started with it

Init

const PmpEngine             = require('pmp-engine');
 
//create engine instance
let pmpEngine               = new PmpEngine();
 
//define your config
let config = {
    bsOptions: {
        proxy: {
            target: 'http://www.thetimes.co.uk/',
            cookies: { stripeDomain: false }
        },
        port:3000,
        cors:true,
        serveStatic: ['./dist'],
        middleware: [],
        rewriteRules: []
    },
    pimpCmds:[
        { 
            url:'*/magazine/*',
            modifs:[`
                $('head').append('<link rel="stylesheet" type="text/css" href="/css/main.min.css">');
                $('body').append('<script type="text/javascript" src="/js/main.min.js"></script>');
                /* the 2 lines above can be replaced by using the staples helper plugin */
                $('body').addClass('sample-pimping');
                $('.Article-content p').html('my crazy lorem replacement');
            `] 
        },
        { 
            url:'*/edition/*',
            modifs:[`
                $('head').append('<link rel="stylesheet" type="text/css" href="/css/main.min.css">');
                $('body').append('<script type="text/javascript" src="/js/main.min.js"></script>');
                $('body').addClass('sample-pimping');
                $('.Article-content').prepend('<img width="100%" src="http://i223.photobucket.com/albums/dd245/2ndsite/The%20Holding%20Pen/6f158ba2.jpg" />');
            `] 
        }
    ],
    plugins: [
        'pmp-plugin-staples'
    ]
};
 
//start engine with config
pmpEngine.start(config);

Config bsOptions

BrowserSync configuration options. Beware that some of these are mandatory (ex:proxy mode or at least an empty middleware array). Check the browserSync documentation for more.

property values
target the proxied URL or host [String]
port the output port number [Number]
cors enables cross origin request headers [Boolean]

Config pimpCmds

Set of rules to be applied when the page's URL match a specific pattern. This is an array that contains pimp commands.

property values
url the exact URL or trigger pattern that will activate the following operations [String]
modifs Array of jQuery operations to manipulate the HTML request response [Array[String]]

important Please notice that all commands samples are multiline strings. Multiline strings are the most practical to use (check the example below).

//mutliline case
modifs:[`
    $('head').append('<link rel="stylesheet" type="text/css" href="/css/main.min.css">');
    $('body').append('<script type="text/javascript" src="/js/main.min.js"></script>');
    $('body').addClass('sample-pimping');
`]
 
//normal quotes, all inline case
modifs:["$('head').append('<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/main.min.css\">'); $('body').append('<script type=\"text/javascript\" src=\"/js/main.min.js\"></script>'); $('body').addClass('sample-pimping');"]
 
//normal quotes, one action per array item
modifs:[
    "$('head').append('<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/main.min.css\">');",
    "$('body').append('<script type=\"text/javascript\" src=\"/js/main.min.js\"></script>');",
    "$('body').addClass('sample-pimping');"
]

Config plugins

Set of pmp-plugins to be applied as helpers (need to be npm installed before use). A plugin provide functions for easing the writing of modification rules, it also provides helper custom tags for your custom HTML templates. Just put packages names in an array under the plugins config field.

Usage (standalone)

If you wanne use the engine without coding around it.

npm start

Usage

After init, the engine can be started, stopped or restarted.

//start engine with config
pmpEngine.start(config);
 
//restart engine with same config
pmpEngine.restart();
 
//restart engine and apply another config
pmpEngine.restart(updatedConfig);
 
//stop engine
pmpEngine.stop();
 
//get all logs outputs (RXjs Observable)
pmpEngine.pmpEngineLogsStream.subscribe(log => {
    console.log(log);
});
 
//get all errors (RXjs Observable)
pmpEngine.pmpEngineErrorsStream.subscribe(err => {
    console.log(err);
});
 
//get pmp engine status (RXjs Observable)
pmpEngine.pmpEngineStatusStream.subscribe(status => {
    console.log(status);
});
 
//get currently applied config
console.log(pmpEngine.currentPimpConfig);
 
//get current status [started, stopped, pending]
console.log(pmpEngine.pmpEngineStatus);

have a look at the test to have more code samples

Examples

In the pimpCmd Object there is a set of operations that will allow you to perform some actions on the request's HTML payload. You are free to do whatever DOM transformation you see fit. Cheerio's jQuery light library is provided to ease the process. And don't worry this helper jQuery only exists in the context of the middleware, it will not pollute the resulting pimped page.

add styles

append a new external stylesheet from your local files. The href path root correspond to the dist folder in your project.

modifs:[`
    $('head').append('<link rel="stylesheet" type="text/css" href="/css/main.min.css">');
`] 

alternatively you could add a style tag directly

modifs:[`
    $('head').append('<style>body { background-color:limegreen; color:goldenrod; }</style>');
`] 

add JS

same principle to add some external javascript files for execution. Works also with inline <script> tags.

modifs:[`
    $('body').append('<script type="text/javascript" src="/js/main.min.js"></script>');
`] 
modifs:[`
    $('head').append('<script>alert('hello world!');</script>');
`] 

add/modify/remove the page's HTML

just regular javascript/jQuery manipulations.

modifs:[`
    //remove all paragraphs
    $('body p').remove();
 
    //deactivate links and replace links texts
    $('body a').attr('href', '#').html('hello i\'m a link');
 
    //add an image (from publicly available CDN) to the fifth paragraph in article content
    $('.Article-content p:eq(5)').prepend('<img width="100%" src="http://i223.photobucket.com/albums/dd245/2ndsite/The%20Holding%20Pen/6f158ba2.jpg" />');
`] 

define your own helper functions to manage HTML partials injections

modifs:[`
    var injectHTMLFile = function(url){
        try {
            return fs.readFileSync(path.join('./dist/html', url), 'utf8');
        } catch (err) {
            // If the type is not missing file, then just throw the error again.
            if (err.code !== 'ENOENT') throw err;
 
            // Handle a file-not-found error
            return '<p class="alert alert-warning">HTML inject file not found: ' + url + '</p>';
        }
    }
 
    //inject HTML partial to the DOM from 'dist/html'
    $('.Article-content').prepend(injectHTMLFile('my-article-summary-to-be-injected.html'));
`]

remove stylesheets

modifs:[`
    //find and remove the external stylesheet for bootstrap minified css
    $('link[href$="bootstrap.min.css"]').remove();
`]

remove scripts

modifs:[`
    //find and remove the external script for bootstrap minified js
    $('script[src$="bootstrap.min.js"]').remove();
`]

remotely control pmp-engine (via websocket)

enabling socket server

new PmpEngine({ ioEnabled:true, host: 'localhost', port: 5000 });
property values default comment
ioEnabled [Boolean] false enables remote control
host [url] 'localhost' socket.io host property
port [number] 5000 socket.io port property

remote control

That will enables you to provide a GUI to interact with the pmp-engine. You can see a minimal socket.io client setting in socket-server.spec.js This functionnality will be leveraged in an upcoming pmp-ui package

Limitations

  • need some tests certainly some weird behaviors now and then 🙉
  • cross domains problems when dealing with https sites. This is baked in security for any browser, may be bypassed by extensions such has this one ... beware this is dangerous ‼️
  • because the modifications are applied on the fly in the HTML page request, it will work only with good ol' full server rendered pages. Pimping SPAs or ajax content need some reverse engineering at best, or is plain impossible.

Readme

Keywords

none

Package Sidebar

Install

npm i pmp-engine

Weekly Downloads

1

Version

2.3.1

License

MIT

Last publish

Collaborators

  • kairos666