Skip to content

Commit

Permalink
Resolves #68: adds ability to render targets in metalsmith.metadata()
Browse files Browse the repository at this point in the history
  • Loading branch information
webketje committed Jun 5, 2023
1 parent e104d3b commit 2b95606
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 28 deletions.
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A Metalsmith plugin to render markdown files to HTML, using [Marked](https://git
## Features

- Compiles `.md` and `.markdown` files in `metalsmith.source()` to HTML.
- Enables rendering file metadata keys to HTML through the [keys option](#rendering-file-metadata)
- Enables rendering file or metalsmith metadata keys to HTML through the [keys option](#rendering-metadata)
- Define a dictionary of markdown globalRefs (for links, images) available to all render targets
- Supports using the markdown library of your choice through the [render option](#using-another-markdown-library)

Expand Down Expand Up @@ -75,17 +75,28 @@ metalsmith.use(
- `render` - Specify a custom render function with the signature `(source, engineOptions, context) => string`. `context` is an object with the signature `{ path:string, key:string }` where the `path` key contains the current file path, and `key` contains the target metadata key.
- `engineOptions` Options to pass to the markdown engine (default [marked](https://github.com/markedjs/marked))

### Rendering file metadata
### Rendering metadata

You can render markdown to HTML in file metadata keys by specifying the `keys` option.
The `keys` option also supports dot-delimited key-paths.
You can render markdown to HTML in file or metalsmith metadata keys by specifying the `keys` option.
The `keys` option also supports dot-delimited key-paths. You can also use [globalRefs within them](#defining-a-dictionary-of-markdown-globalrefs)

```js
metalsmith.use(
markdown({
keys: ['html_desc', 'nested.data']
metalsmith
.metadata({
from_metalsmith_metadata: 'I _shall_ become **markdown** and can even use a [globalref][globalref_link]',
markdownRefs: {
globalref_link: 'https://johndoe.com'
}
})
)
.use(
markdown({
keys: {
files: ['html_desc', 'nested.data'],
global: ['from_metalsmith_metadata']
},
globalRefs: 'markdownRefs'
})
)
```

You can even render all keys at a certain path by setting the `wildcard` option and using a globstar `*` in the keypaths.
Expand Down Expand Up @@ -170,7 +181,7 @@ metalsith
// eg in a markdown file: [My Twitter profile][twitter]
.use(markdown({ globalRefs: 'global.links' }))
// eg in a handlebars layout: {{ global.links.twitter }}
.use(layouts())
.use(layouts({ pattern: '**/*.html' }))
```

### Custom markdown rendering
Expand Down
8 changes: 6 additions & 2 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ export type Render<E> = (source: string, engineOptions: EngineOptions<E>, contex

export type Options<E = marked.MarkedOptions> = {
/**
* - Key names of file metadata to render to HTML - can be nested
* - Array of file metadata key names or object with arrays of key names of file or global metadata key names to render to HTML - can be nested keypaths
*/
keys?: string[];
keys?: string[] | {
files: string[]
global: string[]
};
/**
* - Expand `*` wildcards in keypaths
* @default false
*/
wildcard?: boolean;
/**
Expand Down
47 changes: 31 additions & 16 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function refsObjectToMarkdown(refsObject) {

/**
* @typedef Options
* @property {string[]} [keys] - Key names of file metadata to render to HTML - can be nested
* @property {string[]|{files: string[], global: string[]}} [keys] - Key names of file metadata to render to HTML - can be nested
* @property {boolean} [wildcard=false] - Expand `*` wildcards in keypaths
* @property {string|Object<string, string>} [globalRefs] An object of `{ refname: 'link' }` pairs that will be made available for all markdown files and keys,
* or a `metalsmith.metadata()` keypath containing such object
Expand All @@ -33,7 +33,7 @@ function refsObjectToMarkdown(refsObject) {
**/

const defaultOptions = {
keys: [],
keys: {},
wildcard: false,
render: defaultRender,
engineOptions: {},
Expand All @@ -52,10 +52,31 @@ function markdown(options = defaultOptions) {
options = Object.assign({}, defaultOptions, options)
}

if (Array.isArray(options.keys)) {
options.keys = { files: options.keys }
}

return function markdown(files, metalsmith, done) {
const debug = metalsmith.debug('@metalsmith/markdown')
const matches = metalsmith.match('**/*.{md,markdown}', Object.keys(files))

function renderKeys(keys, prepend, target, path) {
if (options.wildcard) {
keys = expandWildcardKeypaths(target, keys, '*')
}

keys.forEach((key) => {
const value = get(target, key)
if (typeof value === 'string') {
const context = path === 'metalsmith.metadata()' ? { key } : { path, key }
debug.info('Rendering key "%s" of target "%s"', key.join ? key.join('.') : key, path)
set(target, key, options.render(prepend + value, options.engineOptions, context))
} else if (typeof value !== 'undefined') {
debug.warn('Couldn\'t render key "%s" of target "%s": not a string', key.join ? key.join('.') : key, path)
}
})
}

const legacyEngineOptions = Object.keys(options).filter((opt) => !Object.keys(defaultOptions).includes(opt))
if (legacyEngineOptions.length) {
debug.warn('Starting from version 2.0 marked engine options will need to be specified as options.engineOptions')
Expand Down Expand Up @@ -101,25 +122,19 @@ function markdown(options = defaultOptions) {
})
data.contents = Buffer.from(str)

let keys = options.keys
if (options.wildcard) {
keys = expandWildcardKeypaths(data, options.keys, '*')
}
keys.forEach((key) => {
const value = get(data, key)
if (typeof value === 'string') {
debug.info('Rendering key "%s" of file "%s"', key.join ? key.join('.') : key, file)
set(data, key, options.render(globalRefsMarkdown + value, options.engineOptions, { path: file, key }))
// log a warning if the key is defined and of an unexpected type, but not if the property simply is not defined
} else if (typeof value !== 'undefined') {
debug.warn('Couldn\'t render key "%s" of file "%s": not a string', key.join ? key.join('.') : key, file)
}
})
const keys = options.keys && options.keys.files ? options.keys.files : []
renderKeys(keys, globalRefsMarkdown, data, file)

delete files[file]
files[html] = data
})

if (options.keys && options.keys.global) {
debug.info('Processing metalsmith.metadata()')
const meta = metalsmith.metadata()
renderKeys(options.keys.global, globalRefsMarkdown, meta, 'metalsmith.metadata()')
}

done()
}
}
Expand Down
33 changes: 32 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ describe('@metalsmith/markdown', function () {
if (err) done(err)
try {
assert.deepStrictEqual(output.slice(0, 1), [
['warn', 'Couldn\'t render key "%s" of file "%s": not a string', 'not_a_string', 'index.md']
['warn', 'Couldn\'t render key "%s" of target "%s": not a string', 'not_a_string', 'index.md']
])
done()
} catch (err) {
Expand Down Expand Up @@ -326,6 +326,37 @@ describe('@metalsmith/markdown', function () {
})
})

it('should render keys in metalsmith.metadata()', function (done) {
const ms = msCommon('test/fixtures/basic')
ms.env('DEBUG', '@metalsmith/mardown*')
.metadata({
markdownRefs: {
defined_link: 'https://globalref.io'
},
has_markdown: '**[globalref_link][defined_link]**'
})
.use(
markdown({
keys: {
global: ['has_markdown']
},
globalRefs: 'markdownRefs'
})
)
.process((err) => {
if (err) done(err)
try {
assert.strictEqual(
ms.metadata().has_markdown,
'<p><strong><a href="https://globalref.io">globalref_link</a></strong></p>\n'
)
done()
} catch (err) {
done(err)
}
})
})

it('expandWildCardKeyPath should throw if root is not an object', function () {
try {
expandWildcardKeypath(null, [], '*')
Expand Down

0 comments on commit 2b95606

Please sign in to comment.