From 053e92eda7e638ad971ac034072aff1cd3108e8b Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Wed, 13 Dec 2023 00:31:13 +0100 Subject: [PATCH] Allows front-matter defined permalinks to use :placeholder patterns --- src/index.js | 61 ++++++------------- .../services/software-development/index.html | 1 + .../src/test.html | 7 +++ test/index.js | 5 ++ 4 files changed, 33 insertions(+), 41 deletions(-) create mode 100644 test/fixtures/permalink-frontmatter-dynamic/expected/services/software-development/index.html create mode 100644 test/fixtures/permalink-frontmatter-dynamic/src/test.html diff --git a/src/index.js b/src/index.js index 4ff1414..8a80a12 100644 --- a/src/index.js +++ b/src/index.js @@ -171,33 +171,15 @@ const normalizeOptions = (options) => { } } -/** - * Resolve a permalink path string from an existing file `path`. - * - * @param {String} str The path - * @return {String} - */ -const resolve = (str, directoryIndex = 'index.html') => { - const base = path.basename(str, path.extname(str)) - let ret = path.dirname(str) - if (base !== path.basename(directoryIndex, path.extname(directoryIndex))) { - ret = path.join(ret, base).replace(/\\/g, '/') - } - - return ret -} - /** * Replace a `pattern` with a file's `data`. * - * @param {string} pattern (optional) - * @param {Object} data * @param {Object} options + * @param {Object} data * * @return {Mixed} String or Null */ -const replace = (pattern, data, options) => { - if (!pattern) return null +const replace = ({ pattern, ...options }, data) => { // regexparam has logic that interprets a dot as start of an extension name // we don't want this here, so we replace it temporarily with a NUL char const remapped = pattern.replace(/\./g, '\0') @@ -222,8 +204,9 @@ const replace = (pattern, data, options) => { } } - const transformed = route.inject(remapped, ret) - + let transformed = route.inject(remapped, ret) + if (path.basename(transformed) === path.basename(options.directoryIndex, path.extname(options.directoryIndex))) + transformed = path.dirname(transformed) // handle absolute paths if (transformed.startsWith('/')) return transformed.slice(1) return transformed @@ -273,27 +256,28 @@ function permalinks(options) { .filter((file) => files[file].permalink !== false) .forEach((file) => { const data = files[file] + const hasOwnPermalinkDeclaration = !!data.permalink debug('checking file: %s', file) const linkset = findLinkset(data, file, metalsmith) + const permalinkTransformContext = { ...normalizedOptions, ...defaultLinkset, ...linkset } + if (hasOwnPermalinkDeclaration) permalinkTransformContext.pattern = data.permalink debug('applying pattern: %s to file: %s', linkset.pattern, file) let ppath + + // Override the path with `permalink` option. Before the replace call, so placeholders can also be used in front-matter + if (Object.prototype.hasOwnProperty.call(data, 'permalink')) { + ppath = data.permalink + } + try { - ppath = - replace( - linkset.pattern, - { - ...data, - basename: - path.basename(file) === normalizedOptions.directoryIndex - ? '' - : path.basename(file, path.extname(file)), - dirname: path.dirname(file) - }, - { ...normalizedOptions, ...defaultLinkset, ...linkset } - ) || resolve(file, normalizedOptions.directoryIndex) + ppath = replace(permalinkTransformContext, { + ...data, + basename: path.basename(file, path.extname(file)), + dirname: path.dirname(file) === '.' ? '' : path.dirname(file) + }) } catch (err) { return done(new Error(`${err.message} for file '${file}'`)) } @@ -305,11 +289,6 @@ function permalinks(options) { return done(new Error(msg)) } - // Override the path with `permalink` option - if (Object.prototype.hasOwnProperty.call(data, 'permalink')) { - ppath = data.permalink - } - const out = makeUnique(path.normalize(ppath), files, file, normalizedOptions) if (out instanceof Error) { return done(out) @@ -322,7 +301,7 @@ function permalinks(options) { } // contrary to the 2.x "path" property, the permalink property does not override previously set file metadata - if (!data.permalink) { + if (!hasOwnPermalinkDeclaration) { data.permalink = permalink } diff --git a/test/fixtures/permalink-frontmatter-dynamic/expected/services/software-development/index.html b/test/fixtures/permalink-frontmatter-dynamic/expected/services/software-development/index.html new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/test/fixtures/permalink-frontmatter-dynamic/expected/services/software-development/index.html @@ -0,0 +1 @@ +test diff --git a/test/fixtures/permalink-frontmatter-dynamic/src/test.html b/test/fixtures/permalink-frontmatter-dynamic/src/test.html new file mode 100644 index 0000000..85d5434 --- /dev/null +++ b/test/fixtures/permalink-frontmatter-dynamic/src/test.html @@ -0,0 +1,7 @@ +--- +collection: services +service: software-development +permalink: :collection/:service +title: test +--- +test diff --git a/test/index.js b/test/index.js index c7c0894..1143462 100644 --- a/test/index.js +++ b/test/index.js @@ -29,6 +29,11 @@ const fixtures = [ folder: 'permalink-override', options: ':title' }, + { + message: 'should support dynamic permalink from front-matter metadata', + folder: 'permalink-frontmatter-dynamic', + options: undefined + }, { message: 'should remove/replace invalid path characters by default', folder: 'permalink-invalid-chars',