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 🙏

© 2024 – Pkg Stats / Ryan Hefner

pmp-engine

v2.3.1

Published

Pimp My Page BrowserSync based web mod engine

Downloads

6

Readme

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 :innocent:)

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 :hear_no_evil:
  • 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 :bangbang:
  • 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.