slugz

Turn a string into a url-safe slug.

Usage no npm install needed!

<script type="module">
  import slugz from 'https://cdn.skypack.dev/slugz';
</script>

README

slugz

Turn a string into a url-safe slug.

Basic Usage

var slugz = require('slugz')();

slugz('Developing Hybrid iPhone Apps'); // => 'developing-hybrid-iphone-apps'
slugz("It's Johnny!"); // => 'its-johnny'

Cleaners

You can customize the way handles are generated by using cleaners. Cleaners are added using the chainable use method.

use(function)

Any function passed to the use method will be called with a cleaned handle that has underscores seperating words, and is expected to return a modified handle.

For example, the following could be used to replace "yipee" with "yahoo":

var slugz = require('slugz')().use(function(text) {
    return text.replace(/(^|_)yipee(_|$)/g, '_yahoo_');
});

slugz('Things That Make Me Say "Yipee!"'); // => 'things-that-make-me-say-yahoo'

use(object)

If you need more granularity then you can pass an object to use that can have any combination of beforeClean, clean, and afterClean methods.

  • beforeClean is called before the original text has been modified in any way
  • clean is called after basic cleaning has occurred and the text will be underscore delimited
  • afterClean is called just before the final handle is returned

For example, if you want to expand some contractions:

var slugz = require('slugz')().use({
    beforeClean: function(text) {
        return text.replace(/\b(that|what)'s\b/gi, '$1 is');
    }
});

slugz("That's All Folks"); // => 'that-is-all-folks'
slugz("Peyton Manning: What's the Big Deal?"); // => 'peyton-manning-what-is-the-big-deal'

If you only need clean then consider just passing that function to use rather than using an object.

use(string, [params...])

There are several predefined cleaners in the "cleaners" folder that can be be added by name and configured by adding addtional arguments to the use call.

Parenthesis

Strip parenthetical text from handles.

var slugz = require('slugz')().use('parenthesis');

slugz('Save Money on Over-the-Counter (OTC) Medications'); // => 'save-money-on-over-the-counter-medications'

How-To

Remove the words "how to" from handles.

var slugz = require('slugz')().use('how-to');

slugz('How to Build a Hover Car'); // => 'build-a-hover-car'
slugz('HowTo: Dance Like You Mean It'); // => 'dance-like-you-mean-it'
slugz('Healthcare Headlines: How-to Deal with High Blood Pressure'); // => 'healthcare-headlines-deal-with-high-blood-pressure'

Max Chars

Limit the character length of the handle, truncating on word boundaries.

var slugz = require('slugz')().use('max-chars', 20);

slugz("Your Dog's Pain Medications and Muscle Relaxants: Dangerous to Humans"); // => 'your-dogs-pain'

Max Words

Limit the number of words in the handle.

var slugz = require('slugz')().use('max-words', 5);

slugz('Natural Ways to Control High Blood Pressure'); // => 'natural-ways-to-control-high'

Common Words

Remove words that would not be considered "keywords". First and last words will not be touched as well as hyphenated words.

var slugz = require('slugz')().use('common-words');

slugz('Worst NFL Off-Seasons of 2013'); // => 'worst-nfl-off-seasons-2013'
slugz('The Best of Both Worlds'); // => 'the-best-both-worlds'
slugz("Your Dog's Pain Medications and Muscle Relaxants: Dangerous to Humans"); // => 'your-dogs-pain-medications-muscle-relaxants-dangerous-humans'

You can optionally pass your own "common words" list:

var slugz = require('slugz')().use('common-words', {
    remove: ['best', 'hot', 'humans?', 'its', 'the', 'worst', 'your'],
    preserve: ['hot[_\\-]+momma', 'your_+dogs?']
});

slugz('Treating Your Dog With Human Medications'); // => 'treating-your-dog-with-medications'
slugz("Staying Cool When It's Hot Outside"); // => 'staying-cool-when-outside'
slugz('How Your Dog Percieves Humans In Groups'); // => 'how-your-dog-percieves-in-groups'

How we use it:

This is basically the logic that we want to use to generate article handles:

var Handleify = require('slugz'),
    async = require('async');

var MAX_CHARS = 100;

// Handle generators, in our prefered order
var handleifiers = [
    Handleify(),
    Handleify().use('parenthesis'),
    Handleify().use('parenthesis').use('how-to'),
    Handleify().use('common-words'),
    Handleify().use('parenthesis').use('common-words'),
    Handleify().use('how-to').use('common-words'),
    Handleify().use('parenthesis').use('how-to').use('common-words')
];

// Handle generators to fall back to if the corresponding one above is too long
var shortenedHandleifiers = [
    Handleify().use('max-chars', MAX_CHARS),
    Handleify().use('parenthesis').use('max-chars', MAX_CHARS),
    Handleify().use('parenthesis').use('how-to').use('max-chars', MAX_CHARS),
    Handleify().use('common-words').use('max-chars', MAX_CHARS),
    Handleify().use('parenthesis').use('common-words').use('max-chars', MAX_CHARS),
    Handleify().use('how-to').use('common-words').use('max-chars', MAX_CHARS),
    Handleify().use('parenthesis').use('how-to').use('common-words').use('max-chars', MAX_CHARS)
];


function getHandle(headline, done) {
    var results = [],
        shortenedResults = [];

    // Get 7 possible handles to test for uniqueness
    for(var handle, i = 0; i++; i < handleifiers.length) {
        handle = handleifiers[i](headline);

        if(headline.length <= MAX_CHARS)
            results.push(handle);
        else
            shortenedResults.push(shortenedHandleifiers[i](headline));
    }

    // Build the list and remove duplicates
    results = results.concat(shortenedResults.reverse());
    //results = results.unique();

    // Find the first handle that isn't already taken
    async.detect(results, function(handle, cb) {
        mysql.recordExists(handle, function(err, exists) {
            if(err) cb(false);
            else cb(!exists);
        });
    }, done);
}