permalinks
Advanced tools
Comparing version 1.0.0 to 1.0.1
47
index.js
'use strict'; | ||
var use = require('use'); | ||
var handlebars = require('handlebars'); | ||
var convert = require('./lib/convert'); | ||
@@ -35,2 +37,4 @@ var utils = require('./lib/utils'); | ||
this.data = this.options.data || {}; | ||
this.fns = []; | ||
use(this); | ||
} | ||
@@ -106,10 +110,6 @@ | ||
file = utils.formatFile(file, this.options); | ||
file = this.normalizeFile(file, this.options); | ||
var context = this.context(file, locals, this.options); | ||
var pattern = utils.get(file, 'data.permalink.structure') || this.preset(structure); | ||
return this.render(pattern, { | ||
helpers: context.helpers, | ||
data: context.data | ||
}); | ||
return this.render(pattern, context); | ||
}; | ||
@@ -181,3 +181,7 @@ | ||
Permalinks.prototype.helper = function(name, fn) { | ||
this.helpers[name] = fn; | ||
if (name === 'context') { | ||
this.fns.push(fn); | ||
} else { | ||
this.helpers[name] = fn; | ||
} | ||
return this; | ||
@@ -206,6 +210,7 @@ }; | ||
Permalinks.prototype.context = function(file, locals, options) { | ||
var opts = utils.assign({}, this.options, options); | ||
var fileData = utils.assign({}, file.data, file.data.permalink); | ||
var context = utils.assign({}, this.parse(file), this.data, locals, fileData); | ||
var helpers = utils.assign({}, this.helpers); | ||
var ctx = utils.assign({}, {app: this}, {options: options}); | ||
var ctx = utils.assign({}, {app: this}, {options: opts}); | ||
var data = {}; | ||
@@ -229,2 +234,6 @@ | ||
for (var i = 0; i < this.fns.length; i++) { | ||
this.fns[i].call(this, ctx.context); | ||
} | ||
helpers = utils.deepBind(helpers, ctx); | ||
@@ -239,3 +248,3 @@ if (typeof helpers.file === 'function') { | ||
return { | ||
options: options, | ||
options: opts, | ||
helpers: helpers, | ||
@@ -255,3 +264,3 @@ data: data | ||
Permalinks.prototype.render = function(str, options) { | ||
Permalinks.prototype.render = function(structure, options) { | ||
if (!this.helpers.helperMissing) { | ||
@@ -261,5 +270,7 @@ this.helper('helperMissing', helperMissing); | ||
var hbs = utils.handlebars.create(); | ||
var hbs = handlebars.create(); | ||
hbs.registerHelper(options.helpers); | ||
var fn = hbs.compile(convert(str)); | ||
var str = convert(structure); | ||
var fn = hbs.compile(str); | ||
return fn(options.data); | ||
@@ -269,2 +280,14 @@ }; | ||
/** | ||
* | ||
* | ||
* @param {String} `str` | ||
* @param {Object} `options` | ||
* @return {String} Returns the fully resolved permalink string. | ||
*/ | ||
Permalinks.prototype.normalizeFile = function(file, options) { | ||
return utils.normalizeFile(file, options); | ||
}; | ||
/** | ||
* Default helper for managing missing variables | ||
@@ -271,0 +294,0 @@ */ |
@@ -5,4 +5,4 @@ 'use strict'; | ||
module.exports = function(filepath) { | ||
var segs = utils.splitPath(filepath); | ||
module.exports = function(pattern) { | ||
var segs = utils.splitPath(pattern); | ||
var len = segs.length; | ||
@@ -16,3 +16,3 @@ var idx = -1; | ||
function convert(str, last) { | ||
function convert(str, isLast) { | ||
var re = /\\?:(?=[!\w*$_>])(([-!\w*$_>. ]+)(?:\((.*?)\))*[!\w*$_. )]*)/g; | ||
@@ -24,3 +24,3 @@ | ||
if (last && i !== -1) { | ||
if (isLast && i !== -1) { | ||
suffix = args.slice(i); | ||
@@ -31,2 +31,4 @@ args = args.slice(0, i); | ||
args = utils.splitArgs(args).join(''); | ||
args = args.replace(/([*>]) */, '$1 '); | ||
var res = match[0] === '\\' | ||
@@ -36,6 +38,5 @@ ? match.slice(1) | ||
res = res.replace(/([*>]) */, '$1 '); | ||
return res + suffix; | ||
}); | ||
}; | ||
} | ||
@@ -42,0 +43,0 @@ function parse(args) { |
@@ -15,3 +15,2 @@ 'use strict'; | ||
require('get-value', 'get'); | ||
require('handlebars'); | ||
require('isobject', 'isObject'); | ||
@@ -22,3 +21,3 @@ require('parse-filepath', 'parse'); | ||
utils.formatFile = function(val, options) { | ||
utils.normalizeFile = function(val, options) { | ||
function format(file) { | ||
@@ -25,0 +24,0 @@ if (!utils.isObject(file)) { |
{ | ||
"name": "permalinks", | ||
"description": "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.", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"homepage": "https://github.com/jonschlinkert/permalinks", | ||
@@ -30,10 +30,15 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", | ||
"lazy-cache": "^2.0.2", | ||
"lodash.template": "^4.4.0", | ||
"parse-filepath": "^1.0.1", | ||
"use": "^2.0.0", | ||
"vinyl": "^2.0.1" | ||
}, | ||
"devDependencies": { | ||
"diacritics-map": "^0.1.0", | ||
"gulp-format-md": "^0.1.11", | ||
"kind-of": "^3.1.0", | ||
"mocha": "^3.2.0", | ||
"moment": "^2.17.1", | ||
"randomatic": "^1.1.6" | ||
"randomatic": "^1.1.6", | ||
"strip-color": "^0.1.0" | ||
}, | ||
@@ -79,2 +84,2 @@ "keywords": [ | ||
} | ||
} | ||
} |
253
README.md
@@ -26,6 +26,6 @@ # permalinks [![NPM version](https://img.shields.io/npm/v/permalinks.svg?style=flat)](https://www.npmjs.com/package/permalinks) [![NPM monthly downloads](https://img.shields.io/npm/dm/permalinks.svg?style=flat)](https://npmjs.org/package/permalinks) [![NPM total downloads](https://img.shields.io/npm/dt/permalinks.svg?style=flat)](https://npmjs.org/package/permalinks) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/permalinks.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/permalinks) | ||
var file = {path: 'src/about.hbs'}; | ||
var locals = {category: 'foo'}; | ||
var locals = {category: 'blog'}; | ||
console.log(permalinks(structure, file, locals)); | ||
//=> 'foo/about/index.html' | ||
//=> 'blog/about/index.html' | ||
``` | ||
@@ -35,5 +35,5 @@ | ||
<summary><strong>Constructor usage</strong></summary> | ||
If you need to [register helpers](#helpers) or use any of the `Permalinks` methods, | ||
main export can alternatively be used as a constructor function: | ||
The main export can be used as a constructor function. If you need to [register helpers](#helpers) or use any of the `Permalinks` methods, you will need to first create an instance of `Permalinks`. | ||
```js | ||
@@ -44,4 +44,4 @@ var Permalinks = require('permalinks'); | ||
var permalinks = new Permalinks(options); | ||
var file = {path: 'src/about.hbs'}; | ||
var file = new File({path: 'src/about.hbs'}); | ||
console.log(permalinks.format(':stem/index.html', file)); | ||
@@ -52,16 +52,21 @@ //=> 'about/index.html' | ||
## Context | ||
## Files | ||
The "context" is an in-memory object that is used for resolving the placeholders in permalink [structures](#structures). | ||
### File conventions | ||
The context object is created by merging the following objects: | ||
For convenience, files can be defined as a string or an object. If defined as a string, permalinks will convert the filepath to the `file.path` property of an object. | ||
* **file** - all of the [path properties](#file-path-properties) supported by [vinyl](https://github.com/gulpjs/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](#format) method (or the main export if you're not creating an instance of Permalinks) | ||
* **global data** - passed on `options.data` to the contstructor | ||
In other words, both of the following paths: | ||
```js | ||
var file = 'a/b/c.md'; | ||
var file = {path: 'a/b/c.md'}; | ||
``` | ||
...will convert to `{path: 'a/b/c.md'}`. | ||
### File path properties | ||
Values on the provided `file` object are used to resolve placeholders in the permalink structure. File values can be overridden by [locals](#locals) or [helpers](#helpers). | ||
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. | ||
@@ -81,203 +86,111 @@ | ||
### Custom data | ||
## Data | ||
Any of the built-in `file` variable can be overridden by setting the properties directly. | ||
TODO | ||
## Helpers | ||
### locals | ||
Helpers can be used to resolve placeholders in permalink structures. | ||
TODO | ||
### file helper | ||
### file.data | ||
A special built-in `file` helper is called on every file and then removed from the context before rendering. | ||
TODO | ||
```js | ||
permalinks.helper('file', function(file, data, locals) { | ||
// do stuff with file, data and locals | ||
}); | ||
``` | ||
### options.data | ||
This is useful for modifying the context or setting properties on files before generating permalinks. | ||
TODO | ||
<details> | ||
<summary><strong>Example</strong></summary> | ||
```js | ||
var file = new File({path: 'foo/bar/baz.hbs'}); | ||
var permalinks = new Permalinks(); | ||
var count = 0; | ||
## Helpers | ||
permalinks.helper('file', function(file, data, locals) { | ||
data.num = ++count; | ||
}); | ||
Helper functions can be used to resolve placeholders in permalink structures. For example: | ||
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 | ||
```js | ||
var url = permalinks.format(':foo', {path: 'about.hbs'}); | ||
``` | ||
</details> | ||
<details> | ||
<summary><strong>SEO Recommendations</strong></summary> | ||
### Registering helpers | ||
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. | ||
Helpers are registered using the `permalinks.helper()` method. | ||
### Use semantic relevance | ||
```js | ||
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. | ||
permalinks.helper('foo', function() { | ||
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` | ||
<details> | ||
<summary><strong>Helper example</strong></summary> | ||
It's not unusualy for big sites to use different structures for different parts of the site (blog, products, etc). | ||
Use a `date` helper to dynamically generate paths based on the date defined in YAML front matter of a file. | ||
### 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 moment = require('moment'); | ||
var Permalinks = require('permalinks'); | ||
var permalinks = new Permalinks(); | ||
console.log(permalinks.format(':stem/index.html'), {path: 'src/about.hbs'}); | ||
//=> 'about/index.html' | ||
``` | ||
### [.parse](index.js#L64) | ||
var file = { | ||
path: 'src/about.hbs', | ||
data: { | ||
date: '2017-02-14' | ||
} | ||
}; | ||
Uses [parse-filepath](https://github.com/jonschlinkert/parse-filepath) to parse the `file.path` on the given file object. This method is called by the [format](#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. | ||
// "file.data" is merged onto "this.context" | ||
permalinks.helper('date', function(format) { | ||
return moment(this.context.date).format(format || 'YYYY/MM/DD'); | ||
}); | ||
**Params** | ||
* `file` **{Object}** | ||
* `returns` **{Object}** | ||
**Example** | ||
```js | ||
console.log(permalinks.parse({path: 'foo/bar/baz.md'})); | ||
// { root: '', | ||
// dir: 'foo/bar', | ||
// base: 'baz.md', | ||
// ext: '.md', | ||
// name: 'baz', | ||
// extname: '.md', | ||
// basename: 'baz.md', | ||
// dirname: 'foo/bar', | ||
// stem: 'baz', | ||
// path: 'foo/bar/baz.md', | ||
// absolute: [Getter/Setter], | ||
// isAbsolute: [Getter/Setter] } | ||
console.log(permalinks.format(':date/:stem/index.html', file)); | ||
//=> '2017/02/14/about/index.html' | ||
``` | ||
### [.format](index.js#L94) | ||
Helpers can also optionally take arguments: | ||
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](#preset). | ||
* `file` **{Object|String}**: File object or file path string. | ||
* `locals` **{Object}**: Any additional data to use for resolving placeholders. | ||
* `returns` **{String}** | ||
**Example** | ||
```js | ||
var fp = permalinks.format('blog/:stem/index.html', {path: 'src/about.hbs'}); | ||
console.log(fp); | ||
//=> 'blog/about/index.html' | ||
console.log(permalinks.format(':date("YYYY")/:stem/index.html', file)); | ||
//=> '2017/about/index.html' | ||
``` | ||
</details> | ||
### [.preset](index.js#L130) | ||
See the [helper unit tests](test) for more examples. | ||
Define a permalink `preset` with the given `name` and `structure`. | ||
### file helper | ||
**Params** | ||
A special built-in `file` helper is called on every file and then removed from the context before rendering. | ||
* `name` **{String}**: If only the name is passed, | ||
* `structure` **{String}** | ||
* `returns` **{Object}**: Returns the `Permalinks` instance for chaining | ||
**Example** | ||
```js | ||
permalinks.preset('blog', 'blog/:stem/index.html'); | ||
var url = permalinks.format('blog', {path: 'src/about.hbs'}); | ||
console.log(url); | ||
//=> 'blog/about/index.html' | ||
permalinks.helper('file', function(file, data, locals) { | ||
// do stuff with file, data and locals | ||
}); | ||
``` | ||
### [.helper](index.js#L178) | ||
This is useful for modifying the context or setting properties on files before generating permalinks. | ||
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. | ||
<details> | ||
<summary><strong>`file` helper example</strong></summary> | ||
**Params** | ||
Use the `file` helper to increment a value for pagination or something similar: | ||
* `name` **{String}**: Helper name | ||
* `fn` **{Function}** | ||
* `returns` **{Object}**: Returns the Permalink instance for chaining. | ||
**Example** | ||
```js | ||
permalinks.helper('date', function(file, format) { | ||
return moment(file.data.date).format(format); | ||
}); | ||
var file = new File({path: 'foo/bar/baz.hbs'}); | ||
var permalinks = new Permalinks(); | ||
var count = 0; | ||
var structure1 = ':date(file, "YYYY/MM/DD")/:stem/index.html'; | ||
var file1 = permalinks.format(structure1, { | ||
data: {date: '2017-01-01'}, | ||
path: 'src/about.tmpl' | ||
permalinks.helper('file', function(file, data, locals) { | ||
data.num = ++count; | ||
}); | ||
var structure2 = ':name(upper(stem))/index.html'; | ||
var file2 = permalinks.format(structure2, { | ||
data: {date: '2017-01-01'}, | ||
path: 'src/about.tmpl' | ||
}); | ||
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 | ||
``` | ||
console.log(file1); | ||
//=> '2017/01/01/about/index.html' | ||
</details> | ||
console.log(file2); | ||
//=> '2017/01/01/about/index.html' | ||
``` | ||
## About | ||
@@ -321,2 +234,2 @@ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.4.2, on February 14, 2017._ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.4.2, on February 15, 2017._ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
441
0
23037
10
7
229
+ Addedlodash.template@^4.4.0
+ Addeduse@^2.0.0
+ Addeddefine-property@0.2.5(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedis-accessor-descriptor@1.0.1(transitive)
+ Addedis-data-descriptor@1.0.1(transitive)
+ Addedis-descriptor@0.1.7(transitive)
+ Addedlodash._reinterpolate@3.0.0(transitive)
+ Addedlodash.template@4.5.0(transitive)
+ Addedlodash.templatesettings@4.2.0(transitive)
+ Addeduse@2.0.2(transitive)