Code Monkey home page Code Monkey logo

permalinks's Introduction

Metalsmith

npm: version ci: build code coverage license: MIT Gitter chat

An extremely simple, pluggable static site generator for NodeJS.

In Metalsmith, all of the logic is handled by plugins. You simply chain them together.

Here's what the simplest blog looks like:

import { fileURLToPath } from 'node:url'
import { dirname } from 'path'
import Metalsmith from 'metalsmith'
import layouts from '@metalsmith/layouts'
import markdown from '@metalsmith/markdown'

const __dirname = dirname(fileURLToPath(import.meta.url))

Metalsmith(__dirname)
  .use(markdown())
  .use(
    layouts({
      pattern: '**/*.html'
    })
  )
  .build(function (err) {
    if (err) throw err
    console.log('Build finished!')
  })

Installation

NPM:

npm install metalsmith

Yarn:

yarn add metalsmith

Quickstart

What if you want to get fancier by hiding unfinished drafts, grouping posts in collections, and using custom permalinks? Just add plugins...

import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
import Metalsmith from 'metalsmith'
import collections from '@metalsmith/collections'
import layouts from '@metalsmith/layouts'
import markdown from '@metalsmith/markdown'
import permalinks from '@metalsmith/permalinks'
import drafts from '@metalsmith/drafts'

const __dirname = dirname(fileURLToPath(import.meta.url))
const t1 = performance.now()
const devMode = process.env.NODE_ENV === 'development'

Metalsmith(__dirname) // parent directory of this file
  .source('./src') // source directory
  .destination('./build') // destination directory
  .clean(true) // clean destination before
  .env({
    // pass NODE_ENV & other environment variables
    DEBUG: process.env.DEBUG,
    NODE_ENV: process.env.NODE_ENV
  })
  .metadata({
    // add any variable you want & use them in layout-files
    sitename: 'My Static Site & Blog',
    siteurl: 'https://example.com/',
    description: "It's about saying »Hello« to the world.",
    generatorname: 'Metalsmith',
    generatorurl: 'https://metalsmith.io/'
  })
  .use(drafts(devMode)) // only include drafts when NODE_ENV === 'development'
  .use(
    collections({
      // group all blog posts by adding key
      posts: 'posts/*.md' // collections:'posts' to metalsmith.metadata()
    })
  ) // use `collections.posts` in layouts
  .use(
    markdown({
      // transpile all md file contents into html
      keys: ['description'], // and also file.description
      globalRefs: {
        // define links available to all markdown files
        home: 'https://example.com'
      }
    })
  )
  .use(permalinks()) // change URLs to permalink URLs
  .use(
    layouts({
      // wrap layouts around html
      pattern: '**/*.html'
    })
  )
  .build((err) => {
    // build process
    if (err) throw err // error handling is required
    console.log(`Build success in ${((performance.now() - t1) / 1000).toFixed(1)}s`)
  })

How does it work?

Metalsmith works in three simple steps:

  1. Read all the files in a source directory.
  2. Invoke a series of plugins that manipulate the files.
  3. Write the results to a destination directory!

Each plugin is invoked with the contents of the source directory, and each file can contain YAML front-matter that will be attached as metadata, so a simple file like...

---
title: A Catchy Title
date: 2024-01-01
---
An informative article.

...would be parsed into...

{
  'path/to/my-file.md': {
    title: 'A Catchy Title',
    date: new Date(2024, 1, 1),
    contents: Buffer.from('An informative article'),
    stats: fs.Stats
  }
}

...which any of the plugins can then manipulate however they want. Writing plugins is incredibly simple, just take a look at the example drafts plugin.

Of course they can get a lot more complicated too. That's what makes Metalsmith powerful; the plugins can do anything you want!

Plugins

A Metalsmith plugin is a function that is passed the file list, the metalsmith instance, and a done callback. It is often wrapped in a plugin initializer that accepts configuration options.

Check out the official plugin registry at: https://metalsmith.io/plugins.
Find all the core plugins at: https://github.com/search?q=org%3Ametalsmith+metalsmith-plugin
See the draft plugin for a simple plugin example.

API

Check out the full API reference at: https://metalsmith.io/api.

CLI

In addition to a simple Javascript API, the Metalsmith CLI can read configuration from a metalsmith.json file, so that you can build static-site generators similar to Jekyll or Hexo easily. The example blog above would be configured like this:

metalsmith.json

{
  "source": "src",
  "destination": "build",
  "clean": true,
  "metadata": {
    "sitename": "My Static Site & Blog",
    "siteurl": "https://example.com/",
    "description": "It's about saying »Hello« to the world.",
    "generatorname": "Metalsmith",
    "generatorurl": "https://metalsmith.io/"
  },
  "plugins": [
    { "@metalsmith/drafts": true },
    { "@metalsmith/collections": { "posts": "posts/*.md" } },
    { "@metalsmith/markdown": true },
    { "@metalsmith/permalinks": "posts/:title" },
    { "@metalsmith/layouts": true }
  ]
}

Then run:

metalsmith

# Metalsmith · reading configuration from: /path/to/metalsmith.json
# Metalsmith · successfully built to: /path/to/build

Options recognised by metalsmith.json are source, destination, concurrency, metadata, clean and frontmatter. Checkout the static site, Jekyll examples to see the CLI in action.

Local plugins

If you want to use a custom plugin, but feel like it's too domain-specific to be published to the world, you can include plugins as local npm modules: (simply use a relative path from your root directory)

{
  "plugins": [{ "./lib/metalsmith/plugin.js": true }]
}

The secret...

We often refer to Metalsmith as a "static site generator", but it's a lot more than that. Since everything is a plugin, the core library is just an abstraction for manipulating a directory of files.

Which means you could just as easily use it to make...

Resources

Troubleshooting

Set metalsmith.env('DEBUG', '*metalsmith*') to debug your build. This will log debug logs for all plugins using the built-in metalsmith.debug debugger. For older plugins using debug directly, run your build with export DEBUG=metalsmith-*,@metalsmith/* (Linux) or set DEBUG=metalsmith-*,@metalsmith/* for Windows.

Node Version Requirements

Future Metalsmith releases will at least support the oldest supported Node LTS versions.

Metalsmith 2.6.x supports NodeJS versions 14.18.0 and higher.
Metalsmith 2.5.x supports NodeJS versions 12 and higher.
Metalsmith 2.4.x supports NodeJS versions 8 and higher.
Metalsmith 2.3.0 and below support NodeJS versions all the way back to 0.12.

Compatibility & support policy

Metalsmith is supported on all common operating systems (Windows, Linux, Mac). Metalsmith releases adhere to semver (semantic versioning) with 2 minor gray-area exceptions for what could be considered breaking changes:

  • Major Node version support for EOL (End of Life) versions can be dropped in minor releases
  • If a change represents a major improvement that is backwards-compatible with 99% of use cases (not considering outdated plugins), they will be considered eligible for inclusion in minor version updates.

Credits

Special thanks to Ian Storm Taylor, Andrew Meyer, Dominic Barnes, Andrew Goodricke, Ismay Wolff, Kevin Van Lierde and others for their contributions!

permalinks's People

Contributors

ainthek avatar ajedi32 avatar dochang avatar doingweb avatar dpobel avatar gaggle avatar ianstormtaylor avatar jscheel avatar juliangruber avatar lambtron avatar leolabs avatar lygaret avatar mdvorscak avatar pian0 avatar rahulkgupta avatar simbo avatar srcreigh avatar webketje avatar woodyrew avatar xhmikosr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

permalinks's Issues

Default linkset options overwrite further linksets

Describe the bug
When default linkset options are specified, they overwrite specific linksets.

Expected behaviour
It should be the other way around

Credit to @depuits for reporting and proposing a fix for this on the transitional permalinks repo.

Make index.html optional

Would it make sense to make the target filename configurable? For example, when targeting S3, it makes more sense to rename e.g. foo/bar.html to foo/bar (with a Content-Type of text/html) rather than to foo/bar/index.html. That way, you don't get the trailing slash in the URI.

Stuff like metalsmith-serve will most likely choke because of the missing extension, but extensions are basically meaningless anyway, so we should just find a way to set a default MIME type or something.

normalize() has to be run on the linksets to convert their "date" option to a function

When using the recently introduced linksets feature, an error is given: "TypeError: options.date is not a function". This is because normalize() internally is not called on the linksets. As a quickfix I've added find(linksets, function(ls) { normalize(ls) }) below the line var linksets [...] in function plugin [...]. Please either apply this fix or make an alternative one. But without this using the linksets feature is broken AFAIK.

2 tests failing on clean 2.2.0 repo

Describe the bug
2 tests fail when running npm test on a clean clone of the master branch at v2.2.0 94edea2. The relevant output is as follows:

  metalsmith-permalinks
    √ should change files even with no pattern
    √ should replace a pattern
    √ should ignore any files with permalink equal to false option
    √ should override path in any files with permalink option
    √ should accepts a shorthand string
    √ should copy relative files to maintain references
    √ should not copy relative files
    √ should copy relative files even with patterns
    √ should copy relative files once per output file
    1) should copy files in sibling folder
    √ should format a date
    √ should format a date with a custom formatter
    √ should match arbitrary metadata
    √ should use slug by defaults
    √ should use custom slug config if specified
    √ should use custom slug function
    √ should accept options for slug module
    √ should accept options for slug module and create paths
    √ should make urls unique
    √ should allow a custom function for unique urls
    √ should replace any backslashes in paths with slashes
    √ should use the resolve path for false values (not root)
    √ should use the resolve path for empty arrays (not root)
    2) should return an error when clashes happen


  22 passing (149ms)
  2 failing

  1) metalsmith-permalinks
       should copy files in sibling folder:

      Uncaught AssertionError [ERR_ASSERTION]: '<img src="image.jpg">\n' == '<img src="post/image.jpg">\n'
      + expected - actual

      -<img src="image.jpg">
      +<img src="post/image.jpg">

      at C:\IDE SSD\Git\permalinks-test-test\node_modules\assert-dir-equal\lib\index.js:35:14
      at Array.forEach (<anonymous>)
      at assertDirEqual (node_modules\assert-dir-equal\lib\index.js:31:11)
      at Metalsmith.use.build.err (test\index.js:185:11)
      at Immediate.<anonymous> (node_modules\co\index.js:52:14)

  2) metalsmith-permalinks
       should return an error when clashes happen:

      Uncaught AssertionError [ERR_ASSERTION]: 'Permalinks: Clash with another target file one-post\\index.html' == 'Permalinks: Clash with another target file one-post/index.html'
      + expected - actual

      -Permalinks: Clash with another target file one-post\index.html
      +Permalinks: Clash with another target file one-post/index.html

      at Metalsmith.use.build.err (test\index.js:244:16)
      at Immediate.<anonymous> (node_modules\co\index.js:52:14)



npm ERR! Test failed.  See above for more details.

To Reproduce
Steps to reproduce the behaviour:

  1. Clone repo at commit 94edea2
  2. Install dependencies via npm install
  3. Run npm test

Expected behaviour
The expected result is all tests passing in a clean clone of the master branch.

Environment

  • Windows 7 Professional SP1
  • Node v10.13.0
  • npm 6.4.1

Having a dotfile in src/ creates bizarre folder structure

Dotfiles in the source folder cause the permalinks plugin to behave very strangely. There's some sort of unintended recursion going on. Here's a demonstration of a test project first that works fine, then I'll create the dotfile and rebuild to show the error.

smithtest $ make
npm install
node_modules/.bin/metalsmith
  metalsmith-permalinks checking file: one.html +0ms
  metalsmith-permalinks checking file: two.html +5ms
  metalsmith-permalinks checking file: three.html +0ms

  Metalsmith · successfully built to /Users/ianpotter/Development/smithtest/build

smithtest $ ls -R build
2014

build/2014:
3

build/2014/3:
10  16  17

build/2014/3/10:
one

build/2014/3/10/one:
index.html

build/2014/3/16:
two

build/2014/3/16/two:
index.html

build/2014/3/17:
three

build/2014/3/17/three:
index.html

Everything looks good, lets create that errant dotfile and see what happens

smithtest $ touch src/.test
smithtest $ make
npm install
npm WARN package.json [email protected] No description
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] No README data
node_modules/.bin/metalsmith
  metalsmith-permalinks checking file: .test +0ms
  metalsmith-permalinks checking file: one.html +3ms
  metalsmith-permalinks checking file: three.html +3ms
  metalsmith-permalinks checking file: two.html +0ms

  Metalsmith · successfully built to /Users/ianpotter/Development/smithtest/build

smithtest $ ls -R build
2014

build/2014:
3

build/2014/3:
10  16  17

build/2014/3/10:
one

build/2014/3/10/one:
index.html

build/2014/3/16:
two

build/2014/3/16/two:
2014        index.html

build/2014/3/16/two/2014:
3

build/2014/3/16/two/2014/3:
10  17

build/2014/3/16/two/2014/3/10:
one

build/2014/3/16/two/2014/3/10/one:

build/2014/3/16/two/2014/3/17:
three

build/2014/3/16/two/2014/3/17/three:
2014

build/2014/3/16/two/2014/3/17/three/2014:
3

build/2014/3/16/two/2014/3/17/three/2014/3:
10

build/2014/3/16/two/2014/3/17/three/2014/3/10:
one

build/2014/3/16/two/2014/3/17/three/2014/3/10/one:

build/2014/3/17:
three

build/2014/3/17/three:
2014        index.html

build/2014/3/17/three/2014:
3

build/2014/3/17/three/2014/3:
10

build/2014/3/17/three/2014/3/10:
one

build/2014/3/17/three/2014/3/10/one:
smithtest $

YAML Front Matter is Parsed into Types

yaml-js automatically tries to convert some of the yaml front matter into native js types. This leads to errors with permalinks like:

date: 2014-03-09
-------------------------------
Object Sun Mar 09 2014 19:00:00 GMT-0500 (CDT) has no method 'toLowerCase'

as toLowerCase is being called on a Date object.

I think there's a lot of power here though. It would be great to do something like this:

"metalsmith-permalinks": {
    "pattern": ":date_YYYY/:date_MM/:date_DD/:title"
}

Cannot set date permalink format

The docs explain that using :date in permalinks is possible, which is meant to default to 'YYYY/MM/DD', and can be further controlled using http://momentjs.com/docs/#/displaying/format/.

I am trying out Metalsmith, using the blank static site example (from here: https://github.com/segmentio/metalsmith/tree/master/examples/static-site).

I've put :date in permalinks, but whether I add formatting or leave it as default they all come out as full datetime, including the timezone. Like this: thu-aug-23-2012-01-00-00-gmt-0100-bst.

Is this a known issue?

Permalinks should never try to create a folder outside(above) of Metalsmith(__dirname)

Related to #25:

.use(permalinks({
    relative: false,
    pattern: ':collection/:title'
  }))

will try to create about.md, which is NOT in a collection, as /about/index.html, which is the root of the drive on a LInux or OS X system. changing it to:

.use(permalinks({
    relative: false,
    pattern: './:collection/:title'
  }))

makes it relative to something, which happens to be the root of the build dir. What should probably happen is permalinks should always build paths relative to the build dir.

Should expose custom pattern matching

The options do not currently expose the slug-component's option for changing the permalink pattern.

This makes it impossible to add common permalink conditions like changing say, "John's Metalsmith Guide" to "johns-metalsmith-guide" (currently this plugin would instead make it "john-s-metalsmith-guide".)

Or to handle other special characters mid-sentence, i.e., "Metalsmith, Node, and Javascript Are Great" currently becomes "metalsmith-node-javascript-are-great" instead of "metalsmith-node-and-javascript-are-great".

Best solution would be to make the match pattern used for slugs exposed to the options of the metalsmith-permalinks plugin, especially since its slug component already supports the feature.

Conflict with metalsmith-changed

From this issue arve0/metalsmith-changed#11

I've created a function to debug this:

function log () {
  return function (files, metalsmith, done) {
    console.log(Object.keys(files));
    done();
  };
}

In the following config (runned with gulp):

metalsmith(paths.root)
    .metadata(config.metadata || {})
    .source(path.join(paths.src, paths.data))
    .destination(paths.build)
    .clean(false)
    .use(changed())
    .use(markdown())
    .use(log()) //files before
    .use(permalinks())
    .use(log()) //files after
    .build(function(err, files) {
      if (err) {
        console.error(err);
      }
  });
};

The files before execute permalinks are:

[ 'metalsmith-changed-ctimes.json',
  'en.html',
  'es.html',
  'gl.html',
  'index.html',
  'll/en.html' ]

And after:

[ 'metalsmith-changed-ctimes.json',
  'en/index.html',
  'es/index.html',
  'index.html',
  'gl/index.html',
  'll/en/index.html',
  'en/metalsmith-changed-ctimes.json',
  'es/metalsmith-changed-ctimes.json',
  'gl/metalsmith-changed-ctimes.json' ]

So the metalsmith-changed-ctimes.json file is duplicated in each subdirectory.

Generated paths point to root of my HDD

I'm trying to build my project by following this tutorial and I get this error:

{ [Error: EACCES, mkdir '/marcinignac-com'] errno: 3, code: 'EACCES', path: '/marcinignac-com' }

Obviously node doesn't have access to crate folders in root of my HDD

My project tree

com-marcinignac
├── build
├── build.js
├── package.json
├── src
│   ├── css
│   │   └── main.css
│   ├── index.md
│   └── projects
│       ├── cindermedusae
│       │   ├── cindermedusae.md
│       │   └── img
│       │       └── cindermedusae_01.jpg
│       ├── fibers
│       │   └── fibers.md
│       └── ucc-organism
│           └── ucc-organism.md
└── templates
    ├── index.html
    ├── partials
    │   ├── footer.html
    │   └── header.html
    └── post.html

Used modules

    "handlebars": "^2.0.0",
    "metalsmith": "^1.0.1",
    "metalsmith-collections": "^0.6.0",
    "metalsmith-drafts": "0.0.1",
    "metalsmith-markdown": "^0.2.1",
    "metalsmith-permalinks": "^0.3.2",
    "metalsmith-templates": "^0.6.0",
    "metalsmith-watch": "^0.1.1"

Metalsmith config

metalsmith(__dirname)
  .use(collections({
    projects: {
      pattern: 'projects/**/*.md'
    }
  }))
  .use(markdown())
  .use(permalinks({
    pattern: ':collection/:title',
    relative: false
  }))
  .use(templates('handlebars'))
  .destination('./build')
  .build(function(err){
    if (err) throw err;
  });

Dot Notated Patterns

It would be nice if we could do something similar to this for patterns

articles/:series.name/

I would write the code and issue a pull request but appears this repository is not active.

Path contains a backslash and can't be used as URL

Hello, So I've got just one link that's getting a backslash instead of a forward slash it's a bit odd to track down but I suspect it's this plugin. I could be wrong. I'm on windows and I see lots of path.join used in this plugin, should path be building a url safe metadata value as opposed to a filesystem one?

Plugins that could meddle with path;

.use(collections({
      blog:{
        pattern:'blog/**/*.html' // create a collection of blog files by file path
      }
  }))
  .use(permalinks({
    pattern: ':collection/:title',
    relative: false
  }))
  .use(title())

Template;

<ul>
    <li><a href="/">A</a></li>
    <li><a href="/hello">B</a></li>
    <li>subnav
        {{#if blog.length}}
        <ul>
            {{#each blog}}
            <li><a href="/{{path}}">{{title}}</a></li>
            {{/each}}
        </ul>
        {{/if}}
    </li>
</ul>

output, note the "Post two" href;

    <ul>
        <li><a href="/">A</a></li>
        <li><a href="/hello">B</a></li>
        <li>subnav
            <ul>
                <li><a href="/blog/post-one">Post one</a></li>
                <li><a href="/blog\post-two">Post two</a></li>
                <li><a href="/blog/post-the-third">Post, The Third.</a></li>
            </ul>
        </li>
    </ul>
  • Post 1 is a lone html file at blog/post-one.html with a h1 tag.
  • Post 2 is blog/post-two/index.html with a h1 tag
  • Post 3 is blog/third-post/mythrid.html with a title: metadata "Post, the third"

Issue with GitHub Pages

To support custom 404 page for GitHub pages, you need to set "permanlink: /404.html" in the YAML front-matter for a page. Unfortunately, this conflicts with the need to set "permalink: false" to have this plugin ignore the file.

https://help.github.com/articles/custom-404-pages/

Can you ignore the file if the permalink front-matter is already set?

Missing duplicate permalink check

I have a rule that use the post:title property to generate the permalink. When two (or more) post have the same title, the permalink plugin generates only one post. Probably it should add a unique char to the end of the permalink.

E.g.

post-1.md
title: Great news -> great-news
another-post.md
title: Great news -> great-news-2
news-news.md
title: Great news -> great-news-3

...

Can't install

This commit 229392c changes the dependency such that I get the following error installing metalsmith-permalinks:

npm http 304 https://registry.npmjs.org/substitute
npm ERR! notarget No compatible version found: substitute@'segmentio/substitute#0.0.1'
npm ERR! notarget Valid install targets:
npm ERR! notarget ["0.1.0","0.2.0","0.2.2","0.2.3","0.3.0","0.3.1","0.3.2","0.3.3","0.5.0","0.5.1"]
npm ERR! notarget 
npm ERR! notarget This is most likely not a problem with npm itself.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

npm ERR! System Darwin 13.1.0
npm ERR! command "/usr/local/Cellar/node/0.10.26/bin/node" "/usr/local/bin/npm" "install"
npm ERR! cwd /private/tmp/metalsmith-permalinks
npm ERR! node -v v0.10.26
npm ERR! npm -v 1.4.3
npm ERR! code ETARGET

The #0.0.1 should either come out, or add back git://github.com/ — on my computer, both methods work.

Front matter URL/Permalink string contains `/` but is getting converted to `-`

Post front matter is

---
title: Post Title
date: 2018-01-11
permalink: /post/title/
---

Metalsmith config is:

"metalsmith-permalinks": {
  "pattern": "/:permalink"
}

Desired behavior

Jekyll read this front matter & created /post/title/index.html. This is what I would like Metalsmith to do as well.

Actual behavior

The / in the permalink front matter property is converted to -. So this post ends up at /post-title/index.html.

Thanks in advance for your help!

Replace substitute package

The current substitute package isn't published on npm.

We can either get someone to publish it, or we can just replace its functionality. I think the latter will be simpler.

[Question] Can date take alphabets and work same.

just as date form this hierarchy,

---
date: 2017-11-12
---
this results in:

2017/
    11/
        12/
            index.html

I want something like below this:

---
date: usa-newyork-manhattan
---
this results in:

usa/
    newyork/
        manhattan/
                 index.html

Include file directory in metadata

I'd like to be able to include the source relative path part of my template file in my permalink:

ms.use(permalinks(":collection/:dir/:title"))

I'm running into two problems. The first and most pressing is of course that there is no :dir! If I get into the source and play around, I can get :dir to appear no probs, but for posts in the top level directory, I end up with two slashes because :dir is empty. This is also easy fixed by stripping out multiple slashes at the end though.

Would you be willing to accept a pull request for these little tweaks?

permalinks generates unbrowseable urls

Describe the bug
permalinks goal is to generate browseable urls, I've updated the plugin from version 0.5.0 to version 2.1.0 and the plugin is now generating some une browseable urls.

To Reproduce
Steps to reproduce the behaviour:

  1. create some markdown document whose title should be stripped from wrong characters ("user guide (en)", "Cahier d'orientations")
  2. Build the static website
  3. See the generated folder, path are not browseable: user-guide-(en), cahier-d'orientations

Expected behaviour

A browseable path, such as user-guide-en/index.html or cahier-d_orientations/index.html

Environment

  • linux
  • node v10.15.1
  • npm 6.4.1
  • metalsmith 2.3.0
  • metalsmith-permalinks 2.1.0

Commas appear in url/permalink

Though commas are technically legal in the URL path, they're discouraged and look less-than-ideal. I believe they should be stripped.

Willing to come up with a PR for this, but the lack of activity on the repo is discouraging.

Ignore permalink option in metadata of a particular file

It would be nice to be able to ignore the permalinks plugin for specific files. For my use case, aws s3 allows for a static site hosting and can take a top level error.html file as the generic error page. I want to have the error page to not be permalinked. I also would like to take advantage of the template approach, so I don't have to make a unique error.html file in my source directory.

The top yaml of the error.md file would be something like


---
template: error.html
title: error
permalinks-ignore: true

---

Moment throwing errors

When the build runs twice due to browsersync, there's an error getting thrown from moment TypeError: format.replace is not a function

Colon in pattern causes crash or hanged process

Describe the bug
When a colon appears in the metadata referenced by the pattern property, it causes Metalsmith to crash at best, or hang indefinitely at worse, depending on the length of the processed metadata.

Crash message example:

EINVAL: invalid argument, mkdir 'C:\permalinks-bug\build\:'] {
  errno: ←[33m-4071←[39m,
  code: ←[32m'EINVAL'←[39m,
  syscall: ←[32m'mkdir'←[39m,
  path: ←[32m'C:\\permalinks-bug\\build\\:'←[39m
}

To Reproduce
Steps to reproduce the behavior:

  1. Extract permalinks-bug.zip
  2. Run npm install
  3. Run node index

Expected behavior
Special characters in the permalink pattern metadata should be gracefully processed.

Environment

  • Windows 7 SP1
  • Node.js 12.9.0 (includes npm 6.10.2)
  • NTFS file system

Additional context
The issue was introduced in v2.0.0.

[question] aftert or before metalsmith-markdown?

I am not sure if it's even a bug.. So if I do

.use(permalinks())
.use(markdown())

files don't get remapped. Well if I call markdown first then it works. Here are my questions:

  1. I assumed all permalinks do is telling metalsmith that "you should output to dir specified in pattern", then metalsmith would place to that dir when .build() is called, so it doesn't matter to tell metalsmith the output dir before or after md files are parsed?
  2. I aussmed all YAML key value pairs would be ready at the very beginning of plugin chain? Or no? That's why we rely on markdown to parse YAML as well so permalinks can use them with pattern?

so I wonder is it really necessary to remap stuff after markdown is converted to html?

handling german umlauts

At the moment, german umlauts in titles are replaced with dashes, which doesn't result in nicely readable urls. A more "wordpress-like umlaut handling" would be nice:
"ä" replaced width "ae"
"ö" replaced width "oe"
"ü" replaced width "ue"
"ß" replaced width "ss"

I already implemented that in my fork, including tests. Maybe you can merge that...
https://github.com/simbo/metalsmith-permalinks

Front-matter `permalink` override not working

Describe the bug

Adding permalink to the front-matter is not overriding the permalink for the page as described in the Readme.md.

To Reproduce

Create a page with the following front-matter:

---
title: "Hello Metalsmiths"
permalink: "test"
---

Set up build process:

var permalinks = require('metalsmith-permalinks');
..

var siteBuild = metalsmith(__dirname)
  .use(permalinks({
    'pattern': ':title'
  }))
  ...

Expected behaviour
The page should have a permalink of /test. Instead it has a permalink of /hello-metalsmiths

Additional context
It looks like the code that enabled this feature was removed from master. The original feature PR.

The feature works when the code from the PR above is added back to lib/index.js:

if(data.hasOwnProperty('permalink')) {
    path = data['permalink'];
}

"µ" should not appear in a url

When a markdown document has a frontmatter whose title contains a µ character, the url of the document is base on this title and should not contain the µ character. This character should be stripped.

There should also be a lot of other characters that should be stripped from urls.

Process does not terminate

Describe the bug
When calling metalsmith(__dirname).use(permalinks([...])).build(function(err){if (err) throw err;});, the build appears successful and execution continues, but node never terminates upon reaching the end of the main script.

To Reproduce
Steps to reproduce the behavior:

  1. Update all dependencies to their latest version
  2. Create a simple JavaScript file with the above code and valid parameters in the main thread.
  3. Run node with the JavaScript file in parameter

Expected behavior
No pending process should remain after execution preventing node to terminate normally.

Screenshots
N/A

Environment

  • Windows 7 SP1
  • Node.js v11.6.0 with npm 6.5.0-next.0
  • (Also reproduced with Node.js v10.9.0 with npm v6.2.0.)

Additional context
This regression was introduced between v1.0.0 and v2.0.0.
This issue was reproduced even after deleting all of the permalinks parameters except pattern.

relativity and nested source

So I have a source that looks like this:

blog/
    |- posts
        |- a post.html
        |- another post.html
        |-post directory/
            |- index.html
            |- image.jpg

What I'm having a hard time figuring out is how to make it so the permalinks will resolve as:

2014/
    |- 08/
        |- a post/
            |- index.html
        |- another post/
            |- index.html
        |- post directory/
            |- index.html
            |- image.jpg

Any advice?

Note: I've resorted to running permalinks twice, once for top-level .html, and second time for any nested folders separately. This appears to work, but am still curious if there's a better solution. Thanks!

unique option output (both default & custom) are hard to control.

Describe the bug
unique option is hard to control and doesn't work as intended: The order in which the files are passed to the unique function depends on the filesystem -> object keys order (usually alphabetic). See https://github.com/metalsmith/permalinks/runs/2698927437. Currently this makes test runs unpredictable.

Additional context
Suppose a directory with 3 files: a.html, b.html, c.html, all with the title "test".
b.html was added before a.html, so logically we would want b.html to be renamed to test/index.html and a.html to test-1/index.html. Unfortunately this fails. A better metric to choose which filepath to transform would be stats.mtime (for default makeUnique function).

Indicates that this option has rarely been put to use. Due to the structure of the plugin fixing this issue would require a major refactor.

Multiple permalinks per collection

Hello, how do I "map" different permalinks on different collections. For examples I want blog posts to have /YYYY/MM/post-title formatting and regular pages to have /page-title permalink. posts and pages are different collections of course.

Thanks!

Add collaborators or move ownership

Segment don't seem to have time to manage this package, could the Metalsmith Org collaborators be added or aid in the maintenance?

There are—at the time of writing—23 issues and 21 pull requests dating back to 2014!

Duplicating folder/image during build

I'm not sure if this is an issue with permalinks or if it's a product of how metalsmith works. It's quite possible that it's just the way mine is structured. I'm hoping you can shed some light on it.

Okay, I am using Gulp/Metalsmith (gulpsmith). Metalsmith is only going to be handling the images and markdown files. I'm using gulp to process the js, css, etc.

I have a src directory like this:

├─ src
|   ├── javascripts (handled by Gulp)
|   ├── metalsmith (handled by metalsmith/gulpsmith)
|   |   ├── blog
|   |   |   ├── 09-17-2015
|   |   |   |   ├── first-post.md
|   |   |   |   ├── first-post-specific-image.png
|   |   |   ├── index.md
|   |   ├── about
|   |   ├── images
|   |   ├── index.md
|   ├── stylesheets (handled by Gulp)

here's my metalsmith.js file:

import gulp from 'gulp'
import gulpsmith from 'gulpsmith'
import markdown from 'metalsmith-markdown'
import layouts from 'metalsmith-layouts'
import collections from 'metalsmith-collections'
import permalinks from 'metalsmith-permalinks'
import gulpFrontMatter from 'gulp-front-matter'
import connect from 'gulp-connect'
import lodash from 'lodash'
import elevate from 'metalsmith-elevate'

module.exports = function() {
  gulp.src('./src/metalsmith/**/*')
    .pipe(gulpFrontMatter()).on('data', function(file) {
      lodash.assign(file, file.frontMatter);
      delete file.frontMatter;
    })
    .pipe(
      gulpsmith()
        .use(collections({
          articles: {
            pattern: 'blog/!(index.md)**/*.md',
            sortBy: 'date',
            reverse: true
          }
        }))
        .use(markdown())
        .use(layouts({
          engine: 'handlebars',
          directory: 'layouts',
          partials: {
            header: 'partials/header',
            footer: 'partials/footer'
          }
        }))
        .use(permalinks({
          pattern: 'blog/:title'
        }))
    )
    .pipe(gulp.dest('./build'))
    .pipe(connect.reload());
};


So when I run my gulp build task it does its three jobs (process my stylesheets, process my js files, and run the metalsmith task). I have the permalinks pattern referring to blog/:title, and the title is set in the .md file (e.g. My First Post). So metalsmith-permalinks does build it with blog/first-post/index.html and blog/first-post/first-post-specific-image.png, but the image (but not the index.html) is also put in a 09-17-2015 directory.

This is what my /build/blog directory ends up looking like:

├─ build
|   ├── blog
|   |  ├── 09-17-2015
|   |  |  ├── first-post-specific-image.png
|   |  ├── first-post
|   |  |  ├── first-post-specific-image.png
|   |  |  ├── index.html
|   |  ├── index.html

Like I said, I'm not sure if this is metalsmith-permalinks or if it's metalsmith. I'm just hoping someone can shed some light.

Thanks for all the great plugins, by the way!

What is the logic behind relative paths here?

Running metalsmith-permalinks on this directory:

Macbook:project me$ ls -R src/
src:
first-post.html test

src/test:
test.txt

results in the following permalinks structure (with @pian0's duplicate relative files fix)

Macbook:project me$ ls -R build/
build:
first-post test

build/first-post:
index.html test

build/first-post/test:
test.txt

build/test:
test.txt

I'm just wondering what the logic of the copying over the "sibling" files is. IMHO, the resulting file structure should be:

Macbook:project me$ ls -R build/
build:
first-post test

first-post:
index.html

test:
test.txt

The plugin should ignore files with empty metadata ?

In my case, the metalsmith-permalinks plugin runs after other plugins which are adding to my
homepage (/index.html) some YAML metadata this way:

- collection: []
- filename: index

and the pattern is ':collection/:filename'. The result is that the homepage is moved to /index/index.html because the replace(pattern, data, options) function finds all the metadata it needs, even though the metadata is empty.

I managed to make it work by adding

- permalink: false

to my homepage.

Maybe I'm wrong, but I wonder if the permalinks plugin should consider 'empty metadata' just like 'no metadata'

Files being deleted from other plugins

I'm not 100% sure if it is the case, but it seems as if permalinks is replacing folders with the permalink structure defined in a given linkset. For example:

When I run a pagination plugin:

A directory is created with paginated post items like this;

blog/
  1/
    index.html
  2/
    index.html

I then want to permalink each post to be blog/2016/08/23/post-title-here, so I implement permalinks after:

{
        "match": { "collection": "posts" },
        "pattern": "blog/:date/:title"
      },

The result is a single folder:

blog/
   2016/
      08/
        23/
          post-title-here/
            index.html

How can I fix this?

Allow Processing Before Metalsmith-In-Place

It appears that metalsmith-permalinks must be run after metalsmith-in-place as it requires the use of an HTML file

if (!html(file)) return;

I have a plugin that will process the markdown of my files and create indexes in Algolia for searching, this is working perfectly breaking each document up into chunks, however there is one gotcha that I have been hitting.

I have to provide a URL for my Algolia objects so that the search component knows where to link to. There could also be anchor links in my markdown which are processed so I end up with something like this.

[
  {
    "objectID":"articles/getting-started-with-metalsmith/first-page-42329a17ccc7b8eb80eb42d1d558092d",
    "h1":"Starting wtih Metalsmith",
    "link":"articles/getting-started-with-metalsmith/first-page",
    "importance":0
  },
  {
    "objectID":"articles/getting-started-with-metalsmith/first-page#before-we-begin-ae5e3efcdddc387616bbc5b1e5b1b134",
    "h1":"Starting wtih Metalsmith",
    "h3":"Before we Begin",
    "link":"articles/getting-started-with-metalsmith/first-page#before-we-begin",
    "content":"Before we get started I need to make the disclaimer that I am not a Metalsmith expert. The experiences outlined here are experiences I have had along the road to migrating to Metalsmith. If you see something that is incorrect or if you know a better way to do something please speak up and let me know. I am providing this walk-through to get people started with migrating to Metalsmith.",
    "importance":5
  }
]

As you can see there is a link attribute that is sent to Algolia with the json document(s).

Since permalinks cannot be run before metalsmith-in-place I cannot run it prior to my plugin which means I cannot get access to what the final path will be after permalinks is run.

I am trying to make this plugin modular so that anyone could use it and not simply for my project.

Is there any way that I can get at what the permalink would be or would it be tough to allow this to process before metalsmith-in-place? I was going to process the HTML but processing the markdown turned out far easier. I even explored converting the html to markdown and then processing it that way but that was even more tough to process.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.