permalinks
Adds permalink or URL routing/URL rewriting logic to any node.js project. Can be used in static site generators, build systems, web applications or anywhere you need to do path transformation or prop-string replacements.
Install
Install with npm:
$ npm install --save permalinks
Usage
You can add permalinks to any JavaScript project using node's require()
system with the following line of code:
var permalinks = require('permalinks');
To create a permalink, pass a structure
(strong) with placeholders (like :prop
) to replace, and the file
with path information to use:
var structure = ':category/:name/index.html';
var file = {path: 'src/about.hbs'};
var locals = {category: 'foo'};
console.log(permalinks(structure, file, locals));
Constructor usage
If you need to [register helpers](#helpers) or use any of the `Permalinks` methods,
main export can alternatively be used as a constructor function:
var Permalinks = require('permalinks');
var options = {};
var permalinks = new Permalinks(options);
var file = new File({path: 'src/about.hbs'});
console.log(permalinks.format(':stem/index.html', file));
Context
The "context" is an in-memory object that is used for resolving the placeholders in permalink structures.
The context object is created by merging the following objects:
- file - all of the path properties supported by vinyl, and any other user-defined properties defined on the given
file
(if a file is supplied as a string, it will converted to a vinyl file object and used as the file.path
) - file.data - the
file.data
object, if it exists. This object is often used for caching parsed yaml front-matter. - file.data.permalink - This property can be a string, for defining the permalink to structure to use for a file, or an object with optional
structure
and/or options
properties, for defining permalink options and data to be used for a specific file. - locals - passed to the format method (or the main export if you're not creating an instance of Permalinks)
- global data - passed on
options.data
to the contstructor
File path properties
As long as a file is provided with at least a file.path
property, most of the following built-in file
variables will be automatically available on the context.
variable | description |
---|
file.cwd | Gets and sets current working directory. Will always be normalized and have trailing separators removed. Throws when set to any value other than non-empty strings. |
file.base | Gets and sets base directory. Used for created relative paths. When null or undefined , it simply proxies the file.cwd property. Will always be normalized and have trailing separators removed. Throws when set to any value other than non-empty strings or null /undefined . |
file.path | Gets and sets the absolute pathname string or undefined . This value is always normalized and trailing separators are removed. Throws when set to any value other than a string. |
file.relative | Gets the result of path.relative(file.base, file.path) . This is a getter and will throws if set or when file.path is not set. |
file.dirname | Gets and sets the dirname of file.path . Will always be normalized and have trailing separators removed. Throws when file.dirname is not exlicitly defined and/or file.path is not set. |
file.basename | Gets and sets the basename of file.path . Throws when file.basename is not exlicitly defined and/or file.path is not set. |
file.stem | Gets and sets stem (filename without suffix) of file.path . Throws when file.stem is not exlicitly defined and/or file.path is not set. |
file.name | Alias for file.stem |
file.extname | Gets and sets extname of file.path . |
Custom data
Any of the built-in file
variable can be overridden by setting the properties directly.
Helpers
Helpers can be used to resolve placeholders in permalink structures.
file helper
A special built-in file
helper is called on every file and then removed from the context before rendering.
permalinks.helper('file', function(file, data, locals) {
});
This is useful for modifying the context or setting properties on files before generating permalinks.
Example
```js
var file = new File({path: 'foo/bar/baz.hbs'});
var permalinks = new Permalinks();
var count = 0;
permalinks.helper('file', function(file, data, locals) {
data.num = ++count;
});
console.log(permalinks.format(':num-:basename', file));
//=> '1-baz.hbs'
console.log(permalinks.format(':num-:basename', file));
//=> '2-baz.hbs'
console.log(permalinks.format(':num-:basename', file));
//=> '3-baz.hbs'
console.log(count);
//=> 3
</details>
<details>
<summary><strong>SEO Recommendations</strong></summary>
Permalinks are important for SEO, but it's a good idea to spend some time thinking about the strategy you want to use before you decide on a URL structure.
### Use semantic relevance
The most important aspect of a URL is that it makes semantic sense to humans. The more interesting the URL is to humans, the more interesting it will be to search engines.
The following are all good permalink structures, in order of [my own](https://github.com/jonschlinkert) personal preference. Pick the one that makes the most sense for your site:
* `/:postname` (a semantic, descriptive, catchy post name is best permalink structure whenever possible)
* `/:category/:postname/`
* `/:author/:postname` (popular with [medium](https://medium.com)-style blogging platforms)
* `/:category/:author/:postname`
It's not unusualy for big sites to use different structures for different parts of the site (blog, products, etc).
### Avoid date-based permalinks
Contrary to what might seem like an idiomatic pattern, based on the widespread adoption of using dates to categorize blog posts, dates tend to, well... _date_ your content.
Date-based URL's tend to _decrease click through rates_ on older articles. Think about it, who prefers reading out of date content? Try to use a URL strategy that doesn't go out of its way to emphasize the date, and you'll keep your posts feeling like fresh content.
There are plenty of valid use cases for using date-based URL's, like for categorizing movies, albums, breaking news, and so on. But in general, if you're writing about topics that aren't more relevant to users _specifically because of the date of the material_, it's recommend that you avoid using a date-based permalink structure for your blog or documentation, because there is a good chance it will do more harm than good over the long term.
### Numeric permalinks
Numeric or `:id` based permalinks are better than date-based, but they don't really offer much usability or SEO benefit.
## Summary
The best URL structure is one that:
* provides the _highest degree of semantic relevance_ to the content, and
* is _useful to both search engines and humans_
Happy blogging!
</details>
## API
### [Permalinks](index.js#L18)
Create an instance of `Permalinks` with the given `options`
**Params**
* `options` **{Options|String}**
**Example**
```js
var permalinks = new Permalinks();
console.log(permalinks.format(':stem/index.html'), {path: 'src/about.hbs'});
//=> 'about/index.html'
Uses parse-filepath to parse the file.path
on the given file object. This method is called by the format method, but you can use it directly and pass the results as locals
(the last argument) to the .format
method if you need to override or modify any path segments.
Params
file
{Object}returns
{Object}
Example
console.log(permalinks.parse({path: 'foo/bar/baz.md'}));
Generate a permalink by replacing :prop
placeholders in the specified structure
with data from the given file
and locals
.
Params
structure
{String}: Permalink structure or the name of a registered preset.file
{Object|String}: File object or file path string.locals
{Object}: Any additional data to use for resolving placeholders.returns
{String}
Example
var fp = permalinks.format('blog/:stem/index.html', {path: 'src/about.hbs'});
console.log(fp);
Define a permalink preset
with the given name
and structure
.
Params
name
{String}: If only the name is passed,structure
{String}returns
{Object}: Returns the Permalinks
instance for chaining
Example
permalinks.preset('blog', 'blog/:stem/index.html');
var url = permalinks.format('blog', {path: 'src/about.hbs'});
console.log(url);
Define permalink helper name
with the given fn
. Helpers work like any other variable on the context, but they can optionally take any number of arguments and can be nested to build up the resulting string.
Params
name
{String}: Helper namefn
{Function}returns
{Object}: Returns the Permalink instance for chaining.
Example
permalinks.helper('date', function(file, format) {
return moment(file.data.date).format(format);
});
var structure1 = ':date(file, "YYYY/MM/DD")/:stem/index.html';
var file1 = permalinks.format(structure1, {
data: {date: '2017-01-01'},
path: 'src/about.tmpl'
});
var structure2 = ':name(upper(stem))/index.html';
var file2 = permalinks.format(structure2, {
data: {date: '2017-01-01'},
path: 'src/about.tmpl'
});
console.log(file1);
console.log(file2);
About
Contributing
Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Building docs
(This project's readme.md is generated by verb, please don't edit the readme directly. Any changes to the readme must be made in the .verb.md readme template.)
To generate the readme, run the following command:
$ npm install -g verbose/verb
Running tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
$ npm install && npm test
Author
Jon Schlinkert
License
Copyright © 2017, Jon Schlinkert.
MIT
This file was generated by verb-generate-readme, v0.4.2, on February 14, 2017.