You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It is a frequent requirement for plugins to access and manipulate properties on metalsmith metadata or files. Retrieving and setting these properties is not always worry-free. For example, if a plugin wishes to allow users to specify plugin options to be read from a YAML file inside metalsmith.source(), let's say @metalsmith/layouts:
Another need (or inflexibility) is that a lot of plugins "dictate" the key path in the file metadata they read config options from.
For example using @metalsmith/layouts you must specify the key layout: in metadata. This will be ok for some (if not most) users, but other users may prefer to introduce different namespaces in front-matter (eg. translations, config, seo, content). So if the user would prefer reading the layout value from config.layout they should have a way to do that, without putting the onus of implementing the mechanism on the plugin developer. Building on the @metalsmith/layouts example, this would allow
Object.keys(files).forEach(path)=>{metalsmith.set(metalsmith.get(path),'contents',compiled)})// or betterconst{ get, set }=metalsmithObject.keys(files).forEach(path=>{set(get(path),'contents',compiled)})
const{ get, set, metadata }=metalsmithconstblog=get('blog/index.md')constblogTitle=get(blog,'title')set(metadata(),'blog.title',blogTitle)// copy a global metadata property to a fileconstblog=get('blog/index.md')set(blog,'siteurl',metadata().siteurl)// copy a file property to global metadataconstblogTitle=get(blog,'title')set(metadata(),'title',blogTitle)// add a new or overwrite an existing fileset(files,'blog/new.md',{contents: Buffer.from('new or overwritten')})set(blog,'contents',Buffer.from('overwritten'))
Spec
The get/set methods have a base signature of get(context, keypath) and set(context, keypath, value)
The get/set methods should be instance-bound so they can be destructured in plugin bodies: const { get, set } = metalsmith.
The methods would default the context to files, so that it can be omitted and get('blog/index.md') and set('blog/index.md', { contents: Buffer.from('overwritten') }) operate on files.
When the context is equal to files, the methods could accept globs and work as shortcuts for resp. (get) metalsmith.match(glob).map(path => files[path]), always returning an array, and (set) metalsmith.match(glob).forEach(path => { files[path][keypath] = value })
Pros
allows getting a file matching a path with forward slash notation cross-platform metalsmith.get(files, 'nested/index.md').
allows plugin devs to provide more flexibility in specifying options `getOptions: { path: 'config/options.}
Signature
/** * Get a file * @param {string} path * @returns {Metalsmith.File} file at path or undefined **/metalsmith.get(path)/** * Get a property value from context (file metadata, global metadata, or any object) * @param {string} context * @param {string} keypath * @returns {*} Value of the property at context[keypath] or undefined * @example * metalsmith.get(metalsmith.metadata(), 'seo.title') * metalsmith.get(files, 'index.md') * metalsmith.get(files['index.md'], 'title') * metalsmith.get(files['index.md'].collections, 0) **/metalsmith.get(context,keypath)/** * Set a file * @param {string} filepath * @param {string} file * @param {*} value * @returns {Metalsmith} * @throws EPERM when keypath is non-configurable **//** * Set a value to files[filepath][keypath] * @param {string} filepath * @param {string} keypath * @param {*} value * @returns {Metalsmith} * @throws ENOENT when filepath does not exist * @throws EPERM when keypath or filepath is non-configurable **/metalsmith.set(filepath,keypath,value)/** * Set a property value to global metadata * @param {string} keypath * @param {*} value * @returns {Metalsmith} * @throws EPERM when keypath or filepath is non-configurable **/metalsmith.set(metadata,keypath,value)
The text was updated successfully, but these errors were encountered:
Upon rethinking this, the better signature for get/set is an extension of a combo of metalsmith.match() + a keypath getter/setter. For all matched files, it would get/set the properties.
Example:
/** @returns {void} */// for all files matched set type 'post' to the metadatametalsmith.set('posts/*.html','type','post')/** @returns {Array<*>} */// for all files matched get the typemetalsmith.get('{posts,services,products}/*.html','type')
The result returned is always similar to the Metalsmith.Files object.
This could be translated to a CLI command get/set to easily update front-matter with a static website:
metalsmith set"posts/*.html"type post
metalsmith get "{posts,services,products}/*.html"type
Context
It is a frequent requirement for plugins to access and manipulate properties on metalsmith metadata or files. Retrieving and setting these properties is not always worry-free. For example, if a plugin wishes to allow users to specify plugin options to be read from a YAML file inside
metalsmith.source()
, let's say@metalsmith/layouts
:it needs to check if the file is found, and do null-safe gets to nested properties
This could be shortened to:
Another need (or inflexibility) is that a lot of plugins "dictate" the key path in the file metadata they read config options from.
For example using
@metalsmith/layouts
you must specify the keylayout:
in metadata. This will be ok for some (if not most) users, but other users may prefer to introduce different namespaces in front-matter (eg. translations, config, seo, content). So if the user would prefer reading the layout value fromconfig.layout
they should have a way to do that, without putting the onus of implementing the mechanism on the plugin developer. Building on the@metalsmith/layouts
example, this would allowSpec
get(context, keypath)
andset(context, keypath, value)
const { get, set } = metalsmith
.files
, so that it can be omitted andget('blog/index.md')
andset('blog/index.md', { contents: Buffer.from('overwritten') })
operate onfiles
.files
, the methods could accept globs and work as shortcuts for resp. (get)metalsmith.match(glob).map(path => files[path])
, always returning an array, and (set)metalsmith.match(glob).forEach(path => { files[path][keypath] = value })
Pros
metalsmith.get(files, 'nested/index.md')
.Signature
The text was updated successfully, but these errors were encountered: