static-cms/core/src/lib/formatters.ts

155 lines
4.8 KiB
TypeScript
Raw Normal View History

2022-10-20 11:57:30 -04:00
import flow from 'lodash/flow';
import get from 'lodash/get';
import partialRight from 'lodash/partialRight';
2022-09-28 20:04:00 -06:00
import { COMMIT_AUTHOR, COMMIT_DATE } from '../constants/commitProps';
import { sanitizeSlug } from './urlHelper';
2022-10-20 11:57:30 -04:00
import { selectIdentifier, selectInferedField } from './util/collection.util';
import { selectField } from './util/field.util';
import { set } from './util/object.util';
import {
addFileTemplateFields,
compileStringTemplate,
keyToPathArray,
2022-10-20 11:57:30 -04:00
parseDateFromEntry,
} from './widgets/stringTemplate';
import type { Collection, Config, Entry, EntryData, Slug } from '../interface';
const commitMessageTemplates = {
create: 'Create {{collection}} “{{slug}}”',
update: 'Update {{collection}} “{{slug}}”',
delete: 'Delete {{collection}} “{{slug}}”',
uploadMedia: 'Upload “{{path}}”',
deleteMedia: 'Delete “{{path}}”',
} as const;
const variableRegex = /\{\{([^}]+)\}\}/g;
type Options = {
slug?: string;
path?: string;
collection?: Collection;
authorLogin?: string;
authorName?: string;
};
export function commitMessageFormatter(
type: keyof typeof commitMessageTemplates,
2022-10-20 11:57:30 -04:00
config: Config,
{ slug, path, collection, authorLogin, authorName }: Options,
) {
const templates = { ...commitMessageTemplates, ...(config.backend.commit_messages || {}) };
2022-10-01 13:45:01 -04:00
return templates[type].replace(variableRegex, (_, variable) => {
switch (variable) {
case 'slug':
return slug || '';
case 'path':
return path || '';
case 'collection':
2022-10-20 11:57:30 -04:00
return collection ? collection.label_singular || collection.label : '';
case 'author-login':
return authorLogin || '';
case 'author-name':
return authorName || '';
default:
console.warn(`Ignoring unknown variable “${variable}” in commit message template.`);
return '';
}
});
}
export function prepareSlug(slug: string) {
return (
slug
.trim()
// Convert slug to lower-case
.toLocaleLowerCase()
// Remove single quotes.
.replace(/[']/g, '')
// Replace periods with dashes.
.replace(/[.]/g, '-')
);
}
2022-10-20 11:57:30 -04:00
export function getProcessSegment(slugConfig?: Slug, ignoreValues?: string[]) {
return (value: string) =>
ignoreValues && ignoreValues.includes(value)
? value
: flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig)])(value);
}
2022-10-20 11:57:30 -04:00
export function slugFormatter(collection: Collection, entryData: EntryData, slugConfig?: Slug) {
const slugTemplate = collection.slug || '{{slug}}';
2022-10-20 11:57:30 -04:00
const identifier = get(entryData, keyToPathArray(selectIdentifier(collection)));
if (!identifier) {
throw new Error(
'Collection must have a field name that is a valid entry identifier, or must have `identifier_field` set',
);
}
const processSegment = getProcessSegment(slugConfig);
const date = new Date();
const slug = compileStringTemplate(slugTemplate, date, identifier, entryData, processSegment);
2022-10-20 11:57:30 -04:00
if (!('path' in collection)) {
return slug;
} else {
2022-10-20 11:57:30 -04:00
const pathTemplate = prepareSlug(collection.path as string);
return compileStringTemplate(pathTemplate, date, slug, entryData, (value: string) =>
value === slug ? value : processSegment(value),
);
}
}
2022-10-20 11:57:30 -04:00
export function summaryFormatter(summaryTemplate: string, entry: Entry, collection: Collection) {
let entryData = entry.data;
const date = parseDateFromEntry(entry, selectInferedField(collection, 'date')) || null;
const identifier = get(entryData, keyToPathArray(selectIdentifier(collection)));
2022-10-20 11:57:30 -04:00
entryData = addFileTemplateFields(entry.path, entryData, collection.folder) ?? {};
Feat: entry sorting (#3494) * refactor: typescript search actions, add tests avoid duplicate search * refactor: switch from promise chain to async/await in loadEntries * feat: add sorting, initial commit * fix: set isFetching to true on entries request * fix: ui improvments and bug fixes * test: fix tests * feat(backend-gitlab): cache local tree) * fix: fix prop type warning * refactor: code cleanup * feat(backend-bitbucket): add local tree caching support * feat: swtich to orderBy and support multiple sort keys * fix: backoff function * fix: improve backoff * feat: infer sortable fields * feat: fetch file commit metadata - initial commit * feat: extract file author and date, finalize GitLab & Bitbucket * refactor: code cleanup * feat: handle github rate limit errors * refactor: code cleanup * fix(github): add missing author and date when traversing cursor * fix: add missing author and date when traversing cursor * refactor: code cleanup * refactor: code cleanup * refactor: code cleanup * test: fix tests * fix: rebuild local tree when head doesn't exist in remote branch * fix: allow sortable fields to be an empty array * fix: allow translation of built in sort fields * build: fix proxy server build * fix: hide commit author and date fields by default on non git backends * fix(algolia): add listAllEntries method for alogolia integration * fix: handle sort fields overflow * test(bitbucket): re-record some bitbucket e2e tests * test(bitbucket): fix media library test * refactor(gitgateway-gitlab): share request code and handle 404 errors * fix: always show commit date by default * docs: add sortableFields * refactor: code cleanup * improvement: drop multi-sort, rework sort UI * chore: force main package bumps Co-authored-by: Shawn Erquhart <shawn@erquh.art>
2020-04-01 06:13:27 +03:00
// allow commit information in summary template
2022-10-20 11:57:30 -04:00
if (entry.author && !selectField(collection, COMMIT_AUTHOR)) {
entryData = set(entryData, COMMIT_AUTHOR, entry.author);
Feat: entry sorting (#3494) * refactor: typescript search actions, add tests avoid duplicate search * refactor: switch from promise chain to async/await in loadEntries * feat: add sorting, initial commit * fix: set isFetching to true on entries request * fix: ui improvments and bug fixes * test: fix tests * feat(backend-gitlab): cache local tree) * fix: fix prop type warning * refactor: code cleanup * feat(backend-bitbucket): add local tree caching support * feat: swtich to orderBy and support multiple sort keys * fix: backoff function * fix: improve backoff * feat: infer sortable fields * feat: fetch file commit metadata - initial commit * feat: extract file author and date, finalize GitLab & Bitbucket * refactor: code cleanup * feat: handle github rate limit errors * refactor: code cleanup * fix(github): add missing author and date when traversing cursor * fix: add missing author and date when traversing cursor * refactor: code cleanup * refactor: code cleanup * refactor: code cleanup * test: fix tests * fix: rebuild local tree when head doesn't exist in remote branch * fix: allow sortable fields to be an empty array * fix: allow translation of built in sort fields * build: fix proxy server build * fix: hide commit author and date fields by default on non git backends * fix(algolia): add listAllEntries method for alogolia integration * fix: handle sort fields overflow * test(bitbucket): re-record some bitbucket e2e tests * test(bitbucket): fix media library test * refactor(gitgateway-gitlab): share request code and handle 404 errors * fix: always show commit date by default * docs: add sortableFields * refactor: code cleanup * improvement: drop multi-sort, rework sort UI * chore: force main package bumps Co-authored-by: Shawn Erquhart <shawn@erquh.art>
2020-04-01 06:13:27 +03:00
}
2022-10-20 11:57:30 -04:00
if (entry.updatedOn && !selectField(collection, COMMIT_DATE)) {
entryData = set(entryData, COMMIT_DATE, entry.updatedOn);
Feat: entry sorting (#3494) * refactor: typescript search actions, add tests avoid duplicate search * refactor: switch from promise chain to async/await in loadEntries * feat: add sorting, initial commit * fix: set isFetching to true on entries request * fix: ui improvments and bug fixes * test: fix tests * feat(backend-gitlab): cache local tree) * fix: fix prop type warning * refactor: code cleanup * feat(backend-bitbucket): add local tree caching support * feat: swtich to orderBy and support multiple sort keys * fix: backoff function * fix: improve backoff * feat: infer sortable fields * feat: fetch file commit metadata - initial commit * feat: extract file author and date, finalize GitLab & Bitbucket * refactor: code cleanup * feat: handle github rate limit errors * refactor: code cleanup * fix(github): add missing author and date when traversing cursor * fix: add missing author and date when traversing cursor * refactor: code cleanup * refactor: code cleanup * refactor: code cleanup * test: fix tests * fix: rebuild local tree when head doesn't exist in remote branch * fix: allow sortable fields to be an empty array * fix: allow translation of built in sort fields * build: fix proxy server build * fix: hide commit author and date fields by default on non git backends * fix(algolia): add listAllEntries method for alogolia integration * fix: handle sort fields overflow * test(bitbucket): re-record some bitbucket e2e tests * test(bitbucket): fix media library test * refactor(gitgateway-gitlab): share request code and handle 404 errors * fix: always show commit date by default * docs: add sortableFields * refactor: code cleanup * improvement: drop multi-sort, rework sort UI * chore: force main package bumps Co-authored-by: Shawn Erquhart <shawn@erquh.art>
2020-04-01 06:13:27 +03:00
}
const summary = compileStringTemplate(summaryTemplate, date, identifier, entryData);
return summary;
}
export function folderFormatter(
folderTemplate: string,
2022-10-20 11:57:30 -04:00
entry: Entry | undefined,
collection: Collection,
defaultFolder: string,
folderKey: string,
2022-10-20 11:57:30 -04:00
slugConfig?: Slug,
) {
2022-10-20 11:57:30 -04:00
if (!entry || !entry.data) {
return folderTemplate;
}
2022-10-20 11:57:30 -04:00
let fields = set(entry.data, folderKey, defaultFolder) as EntryData;
fields = addFileTemplateFields(entry.path, fields, collection.folder);
2022-10-20 11:57:30 -04:00
const date = parseDateFromEntry(entry, selectInferedField(collection, 'date')) || null;
const identifier = get(fields, keyToPathArray(selectIdentifier(collection)));
const processSegment = getProcessSegment(slugConfig, [defaultFolder, fields?.dirname as string]);
const mediaFolder = compileStringTemplate(
folderTemplate,
date,
identifier,
fields,
processSegment,
);
return mediaFolder;
}