diff --git a/README.md b/README.md index 9f21a68..36af026 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A Metalsmith plugin for setting default values to file metadata. - sets default values for metadata keys and file contents on files matched by pattern - does not overwrite or transform key values that are already defined, unless `strategy: 'overwrite'`. -- can set computed defaults based on other metadata +- can set computed defaults based on other file keys or metalsmith metadata ## Installation @@ -82,7 +82,7 @@ metalsmith.use( `@metalsmith/default-values` takes an array of defaults sets or a single defaults set. The defaults set has the following properties: - `pattern` (`string|string[]`): One or more glob patterns to match file paths. Defaults to `'**'` (all). -- `defaults` (`Object`): An object whose key-value pairs will be added to file metadata. You can also specify a function `callback(file)` to set dynamic defaults based on other, existing file metadata. +- `defaults` (`Object`): An object whose key-value pairs will be added to file metadata. You can also specify a function `callback(file, metadata)` to set dynamic defaults based on existing file or global metadata. - `strategy` (`'keep'|'overwrite'`): Strategy to handle setting defaults to keys that are aleady defined. ### Examples @@ -100,6 +100,32 @@ metalsmith.use( ) ``` +#### Setting dynamic defaults + +You can set dynamic defaults based on current file metadata or metalsmith metadata: + +```js +metalsmith + .metadata({ + build: { timestamp: Date.now() } + }) + .use( + defaultValues([ + { + strategy: 'overwrite', + defaults: { + buildInfo(file, metadata) { + return metadata.build + }, + excerpt(file) { + return file.contents.toString().slice(0, 200) + } + } + } + ]) + ) +``` + #### Combining with other plugins @metalsmith/default-values works great with other `@metalsmith` plugins. The example below attaches a collection and layout matching the parent directory for all files in the directories `services`,`products`, and `articles`: diff --git a/lib/index.d.ts b/lib/index.d.ts index 3d71a06..83c89d1 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,5 +1,4 @@ -import { Plugin } from 'metalsmith'; -import defaultValues from '.'; +import { Plugin, File } from 'metalsmith'; export default defaultValues; export interface DefaultsSet { @@ -7,7 +6,7 @@ export interface DefaultsSet { pattern?: string; /** an object whose keys will be set as file metadata keys */ defaults: { - [key:string]: string; + [key:string]: ((data:File, metadata: {[key:string]:any}) => any)|string|boolean|number|Object; } /** Strategy to handle setting defaults to keys that are aleady defined. */ strategy: 'keep'|'overwrite' diff --git a/src/index.js b/src/index.js index e980dac..6a8e612 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,15 @@ import getDefaultsSetter from './set_defaults.js' +/** + * @callback DefaultSetter + * @param {import('metalsmith').File} file + * @param {Object} metadata + */ + /** * @typedef {Object} DefaultsSet * @property {string|string[]} [pattern="**"] 1 or more glob patterns to match files. Defaults to `'**'` (all). - * @property {Object} [defaults={}] an object whose keys will be set as file metadata keys + * @property {Object} [defaults={}] an object whose keys will be set as file metadata keys * @property {'keep'|'overwrite'} strategy Strategy to handle setting defaults to keys that are aleady defined. */ @@ -50,7 +56,7 @@ function defaultValues(options) { if (matches.length) { const setDefaults = getDefaultsSetter(defaultsEntries, strategy) matches.forEach((file) => { - setDefaults(files[file]) + setDefaults(files[file], metalsmith.metadata()) debug.info( 'Defaults set for file "%s", the resulting metadata is: %O', file, diff --git a/src/set_defaults.js b/src/set_defaults.js index af0f641..d2bb630 100644 --- a/src/set_defaults.js +++ b/src/set_defaults.js @@ -5,10 +5,10 @@ import { dset as set } from 'dset' * Sets defaults for object values * @param {Array>} defaults * @param {'keep'|'overwrite'} strategy - * @return {function} Takes an object and sets defaults + * @return {import('.').DefaultSetter} Takes an object and sets defaults */ function set_defaults(defaults, strategy) { - return (item) => { + return (item, context) => { defaults.forEach(([key, defaultValue]) => { const value = get(item, key) if ( @@ -17,7 +17,7 @@ function set_defaults(defaults, strategy) { value === null || (key === 'contents' && item.contents.toString().trim().length === 0) ) { - if (typeof defaultValue === 'function') defaultValue = defaultValue(item) + if (typeof defaultValue === 'function') defaultValue = defaultValue(item, context) set(item, key, defaultValue) } }) diff --git a/test/index.cjs b/test/index.cjs index 207eea7..c54eb36 100644 --- a/test/index.cjs +++ b/test/index.cjs @@ -22,7 +22,7 @@ function relevantProps(expected, files) { } describe('@metalsmith/default-values', function () { - before(function(done) { + before(function (done) { // eslint-disable-next-line import/no-internal-modules import('../src/set_defaults.js').then(imported => { set_defaults_lib = imported.default @@ -85,11 +85,11 @@ describe('@metalsmith/default-values', function () { it('logs a warning when no files match a pattern', function (done) { let warning - function Debugger() {} + function Debugger() { } Debugger.warn = (msg) => { warning = msg } - Debugger.info = () => {} + Debugger.info = () => { } const msStub = { match() { @@ -245,5 +245,22 @@ describe('@metalsmith/default-values', function () { assert.deepStrictEqual(actual, expected) }) + + it('sets a default computed from additional metadata', function () { + const defaults = { + default_val: true, buildVersion(file, globalMeta) { + return globalMeta.buildVersion + } + } + const set_defaults = set_defaults_lib(Object.entries(defaults)) + const actual = set_defaults({ initial: 'yes' }, { buildVersion: '1.0.0' }) + const expected = { + initial: 'yes', + default_val: true, + buildVersion: '1.0.0' + } + + assert.deepStrictEqual(actual, expected) + }) }) })