├── .gitattributes ├── .travis.yml ├── appveyor.yml ├── .editorconfig ├── .gitignore ├── examples ├── assemblefile.js ├── render.js └── list.js ├── utils.js ├── gulpfile.js ├── LICENSE ├── .verb.md ├── package.json ├── .eslintrc.json ├── index.js ├── readme.md └── test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '7' 9 | - '6' 10 | - '5' 11 | matrix: 12 | fast_finish: true 13 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '7' 4 | - nodejs_version: '6' 5 | - nodejs_version: '5' 6 | install: 7 | - ps: 'Install-Product node $env:nodejs_version' 8 | - npm install 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm test 13 | build: 'off' 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 12 | trim_trailing_whitespace = false 13 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | 29 | examples/*/dist 30 | examples/*/site 31 | -------------------------------------------------------------------------------- /examples/assemblefile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var rimraf = require('rimraf'); 4 | var assemble = require('assemble-core'); 5 | var permalinks = require('..'); 6 | var app = assemble() 7 | app.use(permalinks()); 8 | 9 | app.task('site', function() { 10 | return app.src('./*.js') 11 | .pipe(app.permalink('./site/:name/index.html')) 12 | .pipe(app.dest('.')); 13 | }); 14 | 15 | app.build('site', function(err) { 16 | if (err) throw err; 17 | console.log('done!'); 18 | }); 19 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | 7 | var utils = require('lazy-cache')(require); 8 | var fn = require; 9 | require = utils; 10 | 11 | /** 12 | * Lazily required module dependencies 13 | */ 14 | 15 | require('is-valid-app', 'isValid'); 16 | require('isobject', 'isObject'); 17 | require('mixin-deep', 'merge'); 18 | require('placeholders'); 19 | require('through2', 'through'); 20 | require = fn; 21 | 22 | /** 23 | * Expose `utils` modules 24 | */ 25 | 26 | module.exports = utils; 27 | -------------------------------------------------------------------------------- /examples/render.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var loader = require('assemble-loader'); 4 | var assemble = require('assemble-core'); 5 | var permalinks = require('..'); 6 | 7 | var app = assemble() 8 | .use(permalinks()) 9 | .use(loader()); 10 | 11 | app.create('posts'); 12 | app.posts('./*.js'); 13 | 14 | app.toStream('posts') 15 | .pipe(app.permalink('./site/:name/index.html')) 16 | .pipe(app.dest('.')); 17 | 18 | // app.posts('./*.js') 19 | // app.pipe('posts') 20 | // .pipe(app.permalink('./site/:name/index.html')) 21 | // .pipe(app.dest('.')); 22 | 23 | // console.log(stream) 24 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var mocha = require('gulp-mocha'); 5 | var istanbul = require('gulp-istanbul'); 6 | var eslint = require('gulp-eslint'); 7 | 8 | gulp.task('coverage', function() { 9 | return gulp.src(['index.js', 'utils.js']) 10 | .pipe(istanbul({includeUntested: true})) 11 | .pipe(istanbul.hookRequire()); 12 | }); 13 | 14 | gulp.task('mocha', ['coverage'], function() { 15 | return gulp.src('test.js') 16 | .pipe(mocha()) 17 | .pipe(istanbul.writeReports()); 18 | }); 19 | 20 | gulp.task('eslint', function() { 21 | return gulp.src('*.js') 22 | .pipe(eslint()) 23 | .pipe(eslint.format()); 24 | }); 25 | 26 | gulp.task('default', ['mocha', 'eslint']); 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2017, Jon Schlinkert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | 2 | **HEADS UPS** 3 | 4 | - 0.5.0 has a breaking change. It will no longer immediately run the permalink when used on a view directly. 5 | 6 | ## Usage 7 | 8 | ```js 9 | var permalinks = require('{%= name %}'); 10 | var assemble = require('assemble'); 11 | 12 | var app = assemble(); 13 | 14 | // register the plugin, optionally passing a 15 | // default permalink pattern to use as an argument 16 | // to the plugin 17 | app.use(permalinks()); 18 | ``` 19 | 20 | **Example usage** 21 | 22 | ```js 23 | // create a view collection 24 | app.create('pages'); 25 | 26 | // add a page 27 | app.page('a/b/c.txt', {content: '...'}) 28 | .permalink(':name.html'); 29 | 30 | var page = app.pages.getView('a/b/c.txt'); 31 | console.log(page.data.permalink); 32 | //=> 'c.html' 33 | ``` 34 | 35 | **Pipeline plugin** 36 | 37 | ```js 38 | var posts = app.create('posts') 39 | .use(permalink('actual/:name/index.html')); 40 | 41 | app.task('site', function() { 42 | return posts.src('*.js') 43 | .pipe(posts.permalink()) 44 | .pipe(posts.dest('.')); 45 | }); 46 | 47 | app.build('site', function(err) { 48 | if (err) throw err; 49 | console.log('done!'); 50 | }); 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /examples/list.js: -------------------------------------------------------------------------------- 1 | // 'use strict'; 2 | 3 | // var path = require('path'); 4 | // var assemble = require('assemble-core'); 5 | // var List = assemble.List; 6 | // var permalinks = require('..'); 7 | // var app = assemble(); 8 | 9 | // app.create('post', { 10 | // renameKey: function(key) { 11 | // return path.basename(key, path.extname(key)); 12 | // } 13 | // }); 14 | 15 | // app.create('list') 16 | // .addView('archives', {path: 'index.hbs', content: 'index'}) 17 | // .addView('archive', {path: 'archive.hbs', content: 'archive'}); 18 | 19 | // var archives = app.group('archives', app.posts, app.lists); 20 | 21 | // // placeholders('/site/blog/:path', {path: 'foo-bar.md'}); 22 | // app.data({base: 'dist'}); 23 | // app.posts({ 24 | // 'a/b/c/a.txt': {locals: {base: '_gh_posts/blog'}, content: 'aaa'}, 25 | // 'a/b/c/b.txt': {locals: {base: '_gh_posts/blog'}, content: 'bbb'}, 26 | // 'a/b/c/c.txt': {locals: {base: '_gh_posts/blog'}, content: 'ccc'}, 27 | // 'a/b/c/d.txt': {locals: {base: '_gh_posts/blog'}, content: 'ddd'}, 28 | // 'a/b/c/e.txt': {locals: {base: '_gh_posts/blog'}, content: 'eee'}, 29 | // 'a/b/c/f.txt': {locals: {base: '_gh_posts/blog'}, content: 'fff'}, 30 | // 'a/b/c/g.txt': {locals: {base: '_gh_posts/blog'}, content: 'ggg'}, 31 | // 'a/b/c/h.txt': {locals: {base: '_gh_posts/blog'}, content: 'hhh'}, 32 | // 'a/b/c/i.txt': {locals: {base: '_gh_posts/blog'}, content: 'iii'}, 33 | // 'a/b/c/j.txt': {locals: {base: '_gh_posts/blog'}, content: 'jjj'} 34 | // }); 35 | 36 | // archives.create('archives', { 37 | // all: true, 38 | // paginate: { limit: 3 }, 39 | // permalinks: permalinks(':index(pagination.idx):name.html', { 40 | // index: function(i) { 41 | // return i ? `page/${i + 1}/` : ''; 42 | // } 43 | // }) 44 | // }); 45 | 46 | // Object.keys(app.views.archives).forEach(function(key) { 47 | // var page = app.views.archives[key]; 48 | // console.log(key); 49 | // console.log(page.data); 50 | // console.log(); 51 | // }); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assemble-permalinks", 3 | "description": "Assemble plugin for easily creating permalinks (Assemble ^0.6.0)", 4 | "version": "0.6.1", 5 | "homepage": "https://github.com/assemble/assemble-permalinks", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "contributors": [ 8 | "Brian Woodward (https://twitter.com/doowb)", 9 | "Jon Schlinkert (http://twitter.com/jonschlinkert)" 10 | ], 11 | "repository": "assemble/assemble-permalinks", 12 | "bugs": { 13 | "url": "https://github.com/assemble/assemble-permalinks/issues" 14 | }, 15 | "license": "MIT", 16 | "files": [ 17 | "index.js", 18 | "utils.js" 19 | ], 20 | "main": "index.js", 21 | "engines": { 22 | "node": ">=5.0" 23 | }, 24 | "scripts": { 25 | "test": "mocha" 26 | }, 27 | "dependencies": { 28 | "is-valid-app": "^0.3.0", 29 | "isobject": "^3.0.0", 30 | "lazy-cache": "^2.0.2", 31 | "mixin-deep": "^1.2.0", 32 | "placeholders": "^1.0.0", 33 | "through2": "^2.0.3" 34 | }, 35 | "devDependencies": { 36 | "assemble-core": "^0.31.0", 37 | "assemble-loader": "^1.0.5", 38 | "gulp": "^3.9.1", 39 | "gulp-eslint": "^3.0.1", 40 | "gulp-format-md": "^0.1.9", 41 | "gulp-istanbul": "^1.0.0", 42 | "gulp-mocha": "^2.2.0", 43 | "mocha": "^2.5.3", 44 | "rimraf": "^2.5.3" 45 | }, 46 | "keywords": [ 47 | "assemble", 48 | "assembleplugin", 49 | "blog", 50 | "dest", 51 | "destination", 52 | "filepath", 53 | "format", 54 | "link", 55 | "path", 56 | "permalink", 57 | "permalinks", 58 | "permanent", 59 | "plugin", 60 | "post", 61 | "pretty", 62 | "rewrite", 63 | "rewriting", 64 | "route", 65 | "routes", 66 | "routing", 67 | "uri", 68 | "url", 69 | "url-routing" 70 | ], 71 | "verb": { 72 | "run": true, 73 | "toc": false, 74 | "layout": "default", 75 | "tasks": [ 76 | "readme" 77 | ], 78 | "plugins": [ 79 | "gulp-format-md" 80 | ], 81 | "related": { 82 | "list": [ 83 | "assemble", 84 | "templates" 85 | ] 86 | }, 87 | "reflinks": [ 88 | "verb", 89 | "verb-readme-generator" 90 | ], 91 | "lint": { 92 | "reflinks": true 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "modules": true, 4 | "experimentalObjectRestSpread": true 5 | }, 6 | 7 | "env": { 8 | "browser": false, 9 | "es6": true, 10 | "node": true, 11 | "mocha": true 12 | }, 13 | 14 | "globals": { 15 | "document": false, 16 | "navigator": false, 17 | "window": false 18 | }, 19 | 20 | "rules": { 21 | "accessor-pairs": 2, 22 | "arrow-spacing": [2, { "before": true, "after": true }], 23 | "block-spacing": [2, "always"], 24 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 25 | "comma-dangle": [2, "never"], 26 | "comma-spacing": [2, { "before": false, "after": true }], 27 | "comma-style": [2, "last"], 28 | "constructor-super": 2, 29 | "curly": [2, "multi-line"], 30 | "dot-location": [2, "property"], 31 | "eol-last": 2, 32 | "eqeqeq": [2, "allow-null"], 33 | "generator-star-spacing": [2, { "before": true, "after": true }], 34 | "handle-callback-err": [2, "^(err|error)$" ], 35 | "indent": [2, 2, { "SwitchCase": 1 }], 36 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 37 | "keyword-spacing": [2, { "before": true, "after": true }], 38 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 39 | "new-parens": 2, 40 | "no-array-constructor": 2, 41 | "no-caller": 2, 42 | "no-class-assign": 2, 43 | "no-cond-assign": 2, 44 | "no-const-assign": 2, 45 | "no-control-regex": 2, 46 | "no-debugger": 2, 47 | "no-delete-var": 2, 48 | "no-dupe-args": 2, 49 | "no-dupe-class-members": 2, 50 | "no-dupe-keys": 2, 51 | "no-duplicate-case": 2, 52 | "no-empty-character-class": 2, 53 | "no-eval": 2, 54 | "no-ex-assign": 2, 55 | "no-extend-native": 2, 56 | "no-extra-bind": 2, 57 | "no-extra-boolean-cast": 2, 58 | "no-extra-parens": [2, "functions"], 59 | "no-fallthrough": 2, 60 | "no-floating-decimal": 2, 61 | "no-func-assign": 2, 62 | "no-implied-eval": 2, 63 | "no-inner-declarations": [2, "functions"], 64 | "no-invalid-regexp": 2, 65 | "no-irregular-whitespace": 2, 66 | "no-iterator": 2, 67 | "no-label-var": 2, 68 | "no-labels": 2, 69 | "no-lone-blocks": 2, 70 | "no-mixed-spaces-and-tabs": 2, 71 | "no-multi-spaces": 2, 72 | "no-multi-str": 2, 73 | "no-multiple-empty-lines": [2, { "max": 1 }], 74 | "no-native-reassign": 0, 75 | "no-negated-in-lhs": 2, 76 | "no-new": 2, 77 | "no-new-func": 2, 78 | "no-new-object": 2, 79 | "no-new-require": 2, 80 | "no-new-wrappers": 2, 81 | "no-obj-calls": 2, 82 | "no-octal": 2, 83 | "no-octal-escape": 2, 84 | "no-proto": 0, 85 | "no-redeclare": 2, 86 | "no-regex-spaces": 2, 87 | "no-return-assign": 2, 88 | "no-self-compare": 2, 89 | "no-sequences": 2, 90 | "no-shadow-restricted-names": 2, 91 | "no-spaced-func": 2, 92 | "no-sparse-arrays": 2, 93 | "no-this-before-super": 2, 94 | "no-throw-literal": 2, 95 | "no-trailing-spaces": 0, 96 | "no-undef": 2, 97 | "no-undef-init": 2, 98 | "no-unexpected-multiline": 2, 99 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 100 | "no-unreachable": 2, 101 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 102 | "no-useless-call": 0, 103 | "no-with": 2, 104 | "one-var": [0, { "initialized": "never" }], 105 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 106 | "padded-blocks": [0, "never"], 107 | "quotes": [2, "single", "avoid-escape"], 108 | "radix": 2, 109 | "semi": [2, "always"], 110 | "semi-spacing": [2, { "before": false, "after": true }], 111 | "space-before-blocks": [2, "always"], 112 | "space-before-function-paren": [2, "never"], 113 | "space-in-parens": [2, "never"], 114 | "space-infix-ops": 2, 115 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 116 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 117 | "use-isnan": 2, 118 | "valid-typeof": 2, 119 | "wrap-iife": [2, "any"], 120 | "yoda": [2, "never"] 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * assemble-permalinks 3 | * 4 | * Copyright (c) 2015, 2017, Jon Schlinkert. 5 | * Released under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var path = require('path'); 11 | var utils = require('./utils'); 12 | 13 | /** 14 | * Expose `permalink` 15 | */ 16 | 17 | module.exports = function permalinksPlugin(pattern, config) { 18 | if (utils.isObject(pattern)) { 19 | config = pattern; 20 | pattern = null; 21 | } 22 | 23 | config = utils.merge({ regex: /:([(\w ),.]+)/ }, config); 24 | var args = [].slice.call(arguments); 25 | 26 | return function appPlugin(app) { 27 | if (!utils.isValid(app, 'assemble-permalinks')) { 28 | return collectionPlugin.apply(this, arguments); 29 | } 30 | 31 | app.define('permalink', pipeline(pattern)); 32 | 33 | function collectionPlugin(collection) { 34 | if (collection.isView || collection.isItem) { 35 | return viewPlugin.apply(this, arguments); 36 | } 37 | 38 | if (!utils.isValid(collection, 'assemble-permalinks', ['collection', 'views'])) { 39 | return collectionPlugin; 40 | } 41 | 42 | collection.define('permalink', pipeline(pattern)); 43 | 44 | collection.onLoad(/./, function(file, next) { 45 | if (collection.options.plural !== file.options.collection) { 46 | return next(); 47 | } 48 | 49 | if (typeof pattern === 'string') { 50 | file.permalink.apply(file, args); 51 | } 52 | next(); 53 | }); 54 | 55 | function viewPlugin(view) { 56 | if (!utils.isValid(view, 'assemble-permalinks', ['view', 'item'])) { 57 | return; 58 | } 59 | 60 | this.define('permalink', function(dest, opts) { 61 | if (typeof dest !== 'string') { 62 | opts = dest; 63 | dest = null; 64 | } 65 | 66 | this.emit('permalink', this); 67 | 68 | var options = utils.merge({}, config, this.options.permalinks); 69 | var ctx = utils.merge({}, options, this.data, opts); 70 | var parse = ctx.parsePath || this.parsePath; 71 | var paths = copyPaths(this, parse); 72 | 73 | // merge in paths before context, so custom values 74 | // passed on the options will override parsed values 75 | ctx = utils.merge({}, paths, ctx); 76 | 77 | try { 78 | var fn = utils.placeholders(ctx); 79 | pattern = dest || ctx.pattern || ':path'; 80 | 81 | // set the pattern on `options.permalink` 82 | this.options.permalink = pattern; 83 | 84 | // add the rendered permalink (path) to `data.permalink` 85 | this.data.permalink = fn(pattern, ctx); 86 | 87 | } catch (err) { 88 | err.reason = 'permalinks parsing error'; 89 | throw err; 90 | } 91 | return this; 92 | }); 93 | }; 94 | 95 | return viewPlugin; 96 | }; 97 | 98 | return collectionPlugin; 99 | }; 100 | }; 101 | 102 | function pipeline(pattern) { 103 | return function(viewPattern, data) { 104 | return utils.through.obj(function(view, enc, next) { 105 | var structure = viewPattern || pattern; 106 | try { 107 | view.permalink(structure, data); 108 | var fp = path.resolve(view.data.permalink); 109 | view.path = path.resolve(view.base, fp); 110 | next(null, view); 111 | } catch (err) { 112 | next(err); 113 | } 114 | }); 115 | }; 116 | } 117 | 118 | /** 119 | * Since views are vinyl files and paths are getters/setters, 120 | * we need to copy paths from `view` onto a plain object 121 | */ 122 | 123 | function copyPaths(view, fn) { 124 | var paths = {}; 125 | paths.cwd = view.cwd; 126 | paths.base = view.base; 127 | paths.path = view.path; 128 | paths.absolute = path.resolve(view.path); 129 | paths.dirname = view.dirname; 130 | paths.relative = view.relative; 131 | paths.basename = view.basename; 132 | paths.extname = view.extname; 133 | paths.ext = view.extname; 134 | 135 | paths.filename = view.stem; 136 | paths.name = view.stem; 137 | paths.stem = view.stem; 138 | if (typeof fn === 'function') { 139 | fn(paths); 140 | } 141 | return paths; 142 | } 143 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # assemble-permalinks [![NPM version](https://img.shields.io/npm/v/assemble-permalinks.svg?style=flat)](https://www.npmjs.com/package/assemble-permalinks) [![NPM monthly downloads](https://img.shields.io/npm/dm/assemble-permalinks.svg?style=flat)](https://npmjs.org/package/assemble-permalinks) [![NPM total downloads](https://img.shields.io/npm/dt/assemble-permalinks.svg?style=flat)](https://npmjs.org/package/assemble-permalinks) [![Linux Build Status](https://img.shields.io/travis/assemble/assemble-permalinks.svg?style=flat&label=Travis)](https://travis-ci.org/assemble/assemble-permalinks) [![Windows Build Status](https://img.shields.io/appveyor/ci/assemble/assemble-permalinks.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/assemble/assemble-permalinks) 2 | 3 | > Assemble plugin for easily creating permalinks (Assemble ^0.6.0) 4 | 5 | ## Install 6 | 7 | Install with [npm](https://www.npmjs.com/): 8 | 9 | ```sh 10 | $ npm install --save assemble-permalinks 11 | ``` 12 | 13 | Install with [yarn](https://yarnpkg.com): 14 | 15 | ```sh 16 | $ yarn add assemble-permalinks 17 | ``` 18 | 19 | **HEADS UPS** 20 | 21 | * 0.5.0 has a breaking change. It will no longer immediately run the permalink when used on a view directly. 22 | 23 | ## Usage 24 | 25 | ```js 26 | var permalinks = require('assemble-permalinks'); 27 | var assemble = require('assemble'); 28 | 29 | var app = assemble(); 30 | 31 | // register the plugin, optionally passing a 32 | // default permalink pattern to use as an argument 33 | // to the plugin 34 | app.use(permalinks()); 35 | ``` 36 | 37 | **Example usage** 38 | 39 | ```js 40 | // create a view collection 41 | app.create('pages'); 42 | 43 | // add a page 44 | app.page('a/b/c.txt', {content: '...'}) 45 | .permalink(':name.html'); 46 | 47 | var page = app.pages.getView('a/b/c.txt'); 48 | console.log(page.data.permalink); 49 | //=> 'c.html' 50 | ``` 51 | 52 | **Pipeline plugin** 53 | 54 | ```js 55 | var posts = app.create('posts') 56 | .use(permalink('actual/:name/index.html')); 57 | 58 | app.task('site', function() { 59 | return posts.src('*.js') 60 | .pipe(posts.permalink()) 61 | .pipe(posts.dest('.')); 62 | }); 63 | 64 | app.build('site', function(err) { 65 | if (err) throw err; 66 | console.log('done!'); 67 | }); 68 | ``` 69 | 70 | ## About 71 | 72 | ### Related projects 73 | 74 | * [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit") 75 | * [templates](https://www.npmjs.com/package/templates): System for creating and managing template collections, and rendering templates with any node.js template engine… [more](https://github.com/jonschlinkert/templates) | [homepage](https://github.com/jonschlinkert/templates "System for creating and managing template collections, and rendering templates with any node.js template engine. Can be used as the basis for creating a static site generator or blog framework.") 76 | 77 | ### Contributing 78 | 79 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 80 | 81 | ### Contributors 82 | 83 | | **Commits** | **Contributor** | 84 | | --- | --- | 85 | | 26 | [doowb](https://github.com/doowb) | 86 | | 14 | [jonschlinkert](https://github.com/jonschlinkert) | 87 | 88 | ### Building docs 89 | 90 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 91 | 92 | To generate the readme, run the following command: 93 | 94 | ```sh 95 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 96 | ``` 97 | 98 | ### Running tests 99 | 100 | 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: 101 | 102 | ```sh 103 | $ npm install && npm test 104 | ``` 105 | 106 | ### Author 107 | 108 | **Jon Schlinkert** 109 | 110 | * [github/jonschlinkert](https://github.com/jonschlinkert) 111 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 112 | 113 | ### License 114 | 115 | Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). 116 | Released under the [MIT License](LICENSE). 117 | 118 | *** 119 | 120 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 17, 2017._ -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var assert = require('assert'); 5 | var rimraf = require('rimraf'); 6 | var assemble = require('assemble-core'); 7 | var permalink = require('./'); 8 | var app; 9 | 10 | describe('permalinks', function() { 11 | beforeEach(function() { 12 | app = assemble(); 13 | }); 14 | 15 | it('should support passing the plugin to the app instance:', function() { 16 | app.use(permalink()); 17 | app.create('pages'); 18 | 19 | var page = app.page('a/b/c.txt', {content: '...'}) 20 | .permalink(':name.html'); 21 | 22 | assert(typeof page.permalink === 'function'); 23 | assert(page.data.permalink === 'c.html'); 24 | }); 25 | 26 | it('should emit the plugin on `app`', function(cb) { 27 | app.on('plugin', function(plugin) { 28 | assert.equal(plugin, 'assemble-permalinks'); 29 | cb(); 30 | }); 31 | app.use(permalink()); 32 | }); 33 | 34 | it('should emit the plugin instance', function(cb) { 35 | app.on('plugin', function(plugin) { 36 | assert(this.isApp); 37 | cb(); 38 | }); 39 | app.use(permalink()); 40 | }); 41 | 42 | it('should support passing the plugin to a collection instance:', function() { 43 | app.create('posts') 44 | .use(permalink()); 45 | 46 | app.posts('a/b/aaa.txt', {content: '...'}); 47 | 48 | var post = app.posts.getView('a/b/aaa.txt') 49 | .permalink(':name.html'); 50 | 51 | assert(typeof post.permalink === 'function'); 52 | assert(post.data.permalink === 'aaa.html'); 53 | }); 54 | 55 | it('should support passing the plugin to a view:', function() { 56 | app.create('posts'); 57 | 58 | app.post('a/b/aaa.txt', {content: '...'}); 59 | app.post('a/b/bbb.txt', {content: '...'}); 60 | app.post('a/b/ccc.txt', {content: '...'}); 61 | 62 | var post = app.posts.getView('a/b/aaa.txt') 63 | .use(permalink()) 64 | .permalink(':name.html'); 65 | 66 | assert(typeof post.permalink === 'function'); 67 | assert(post.data.permalink === 'aaa.html'); 68 | }); 69 | 70 | it.skip('should support passing the plugin to a view and executing immediately:', function() { 71 | app.create('posts'); 72 | 73 | app.post('a/b/aaa.txt', {content: '...'}); 74 | app.post('a/b/bbb.txt', {content: '...'}); 75 | app.post('a/b/ccc.txt', {content: '...'}); 76 | 77 | var post = app.posts.getView('a/b/aaa.txt') 78 | .use(permalink(':name.html')); 79 | 80 | assert(typeof post.permalink === 'function'); 81 | assert(post.data.permalink === 'aaa.html'); 82 | }); 83 | 84 | it('should support passing the pattern to the plugin', function() { 85 | app.create('posts') 86 | .use(permalink('foo/:name.html')); 87 | 88 | app.post('a/b/aaa.txt', {content: '...'}); 89 | app.post('a/b/bbb.txt', {content: '...'}); 90 | app.post('a/b/ccc.txt', {content: '...'}); 91 | 92 | var post = app.posts.getView('a/b/aaa.txt'); 93 | 94 | assert(typeof post.permalink === 'function'); 95 | assert(post.data.permalink === 'foo/aaa.html'); 96 | }); 97 | 98 | it('should support passing the config to the plugin', function() { 99 | app.create('posts') 100 | .use(permalink({ 101 | foo: function(name) { 102 | return 'bar-' + name + '.html'; 103 | } 104 | })); 105 | 106 | app.post('a/b/aaa.txt', {content: '...'}); 107 | app.post('a/b/bbb.txt', {content: '...'}); 108 | app.post('a/b/ccc.txt', {content: '...'}); 109 | 110 | var post = app.posts.getView('a/b/aaa.txt'); 111 | assert(typeof post.permalink === 'function'); 112 | 113 | post.permalink('dist/:foo(name)'); 114 | assert(post.data.permalink === 'dist/bar-aaa.html'); 115 | }); 116 | 117 | it('should expose a `permalink` method on `app`', function() { 118 | app.use(permalink()); 119 | assert(typeof app.permalink === 'function'); 120 | }); 121 | 122 | it('should use `app.permalink()` as a pipeline plugin', function(cb) { 123 | app.use(permalink()); 124 | 125 | app.task('site', function() { 126 | return app.src('*.js') 127 | .pipe(app.permalink('actual/:name/index.html')) 128 | .pipe(app.dest('.')); 129 | }); 130 | 131 | app.build('site', function(err) { 132 | if (err) return cb(err); 133 | rimraf('actual', cb); 134 | }); 135 | }); 136 | 137 | it('should handle errors in the pipeline plugin', function(cb) { 138 | app.use(permalink({ 139 | foo: function(name) { 140 | return 'bar-' + name; 141 | } 142 | })); 143 | 144 | app.task('site', function() { 145 | return app.src('*.js') 146 | .pipe(app.permalink('actual/:foo(:name)/index.html')) 147 | .on('error', function(err) { 148 | if (err) cb(); 149 | }) 150 | .pipe(app.dest('.')); 151 | }); 152 | 153 | app.build('site', function(err) { 154 | if (err) return cb(err); 155 | rimraf('actual', cb); 156 | }); 157 | }); 158 | 159 | it('should append permalink to dest path', function(cb) { 160 | app.use(permalink()); 161 | 162 | app.task('site', function() { 163 | return app.src('*.js') 164 | .pipe(app.permalink(':name/index.html')) 165 | .pipe(app.dest('actual/')); 166 | }); 167 | 168 | app.build('site', function(err) { 169 | if (err) return cb(err); 170 | rimraf('actual', cb); 171 | }); 172 | }); 173 | 174 | it('should expose a `permalink` method on a collection', function() { 175 | app.create('posts') 176 | .use(permalink('foo/:name.html')); 177 | 178 | assert(typeof app.posts.permalink === 'function'); 179 | }); 180 | 181 | it('should use the permalink pattern passed to the collection plugin', function(cb) { 182 | var posts = app.create('posts') 183 | .use(permalink('actual/:name/index.html')); 184 | 185 | app.task('site', function() { 186 | return posts.src('*.js') 187 | .pipe(posts.permalink()) 188 | .pipe(posts.dest('.')); 189 | }); 190 | 191 | app.build('site', function(err) { 192 | if (err) return cb(err); 193 | rimraf('actual/', cb); 194 | }); 195 | }); 196 | 197 | it('should append the plugin pattern to the dest path', function(cb) { 198 | var posts = app.create('posts') 199 | .use(permalink(':name/index.html')); 200 | 201 | app.task('site', function() { 202 | return posts.src('*.js') 203 | .pipe(posts.permalink()) 204 | .pipe(posts.dest('actual/')); 205 | }); 206 | 207 | app.build('site', function(err) { 208 | if (err) return cb(err); 209 | rimraf('actual/', cb); 210 | }); 211 | }); 212 | 213 | it('should use permalink locals', function() { 214 | app.create('posts') 215 | .use(permalink()); 216 | 217 | var post = app.post('a/b/aaa.txt', {content: '...'}) 218 | .permalink(':dirname/:foo/:baz.html', { 219 | foo: 'bar', 220 | baz: 'qux' 221 | }); 222 | 223 | assert(typeof post.permalink === 'function'); 224 | assert(post.data.permalink === 'a/b/bar/qux.html'); 225 | }); 226 | 227 | it('should use custom parsePath option', function() { 228 | app.create('posts') 229 | .use(permalink({ 230 | parsePath: function(paths) { 231 | paths.foo = 'bar-' + paths.name; 232 | } 233 | })); 234 | 235 | var post = app.post('a/b/aaa.txt', {content: '...'}) 236 | .permalink(':dirname/:foo.html'); 237 | 238 | assert(typeof post.permalink === 'function'); 239 | assert(post.data.permalink === 'a/b/bar-aaa.html'); 240 | }); 241 | }); 242 | --------------------------------------------------------------------------------