mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-07-05 17:31:22 +02:00
Merge branch 'dev' into issue/666
This commit is contained in:
@@ -23,7 +23,17 @@ import {
|
||||
} from '../constants';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { FrontMatterParser, ParsedFrontMatter } from '../parsers';
|
||||
import { ContentType, Extension, Logger, Settings, SlugHelper, isValidFile, parseWinPath } from '.';
|
||||
import {
|
||||
ContentType,
|
||||
Extension,
|
||||
Logger,
|
||||
Settings,
|
||||
SlugHelper,
|
||||
isValidFile,
|
||||
parseWinPath,
|
||||
processArticlePlaceholdersFromPath,
|
||||
processTimePlaceholders
|
||||
} from '.';
|
||||
import { format, parse } from 'date-fns';
|
||||
import { Notifications } from './Notifications';
|
||||
import { Article } from '../commands';
|
||||
@@ -37,7 +47,6 @@ import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { Link, Parent } from 'mdast-util-from-markdown/lib';
|
||||
import { Content } from 'mdast';
|
||||
import { processKnownPlaceholders } from './PlaceholderHelper';
|
||||
import { CustomScript } from './CustomScript';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { existsAsync, readFileAsync } from '../utils';
|
||||
@@ -57,6 +66,19 @@ export class ArticleHelper {
|
||||
return ArticleHelper.getFrontMatterFromDocument(editor.document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the front matter from the current active document.
|
||||
* @returns The front matter object if found, otherwise undefined.
|
||||
*/
|
||||
public static getFrontMatterFromCurrentDocument() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
return ArticleHelper.getFrontMatterFromDocument(editor.document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of the specified document
|
||||
*
|
||||
@@ -524,7 +546,12 @@ export class ArticleHelper {
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
public static async updatePlaceholders(data: any, title: string, filePath: string) {
|
||||
public static async updatePlaceholders(
|
||||
data: any,
|
||||
title: string,
|
||||
filePath: string,
|
||||
slugTemplate?: string
|
||||
) {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
const fmData = Object.assign({}, data);
|
||||
|
||||
@@ -536,10 +563,11 @@ export class ArticleHelper {
|
||||
}
|
||||
|
||||
if (fieldName === 'slug' && (fieldValue === null || fieldValue === '')) {
|
||||
fmData[fieldName] = SlugHelper.createSlug(title);
|
||||
fmData[fieldName] = SlugHelper.createSlug(title, fmData, slugTemplate);
|
||||
}
|
||||
|
||||
fmData[fieldName] = processKnownPlaceholders(fmData[fieldName], title, dateFormat);
|
||||
fmData[fieldName] = await processArticlePlaceholdersFromPath(fmData[fieldName], filePath);
|
||||
fmData[fieldName] = processTimePlaceholders(fmData[fieldName], dateFormat);
|
||||
fmData[fieldName] = await this.processCustomPlaceholders(fmData[fieldName], title, filePath);
|
||||
}
|
||||
|
||||
@@ -597,7 +625,11 @@ export class ArticleHelper {
|
||||
}
|
||||
|
||||
const regex = new RegExp(`{{${placeholder.id}}}`, 'g');
|
||||
const updatedValue = processKnownPlaceholders(placeHolderValue, title, dateFormat);
|
||||
let updatedValue = filePath
|
||||
? await processArticlePlaceholdersFromPath(placeHolderValue, filePath)
|
||||
: placeHolderValue;
|
||||
|
||||
updatedValue = processTimePlaceholders(updatedValue, dateFormat);
|
||||
|
||||
if (value === `{{${placeholder.id}}}`) {
|
||||
value = updatedValue;
|
||||
|
||||
+71
-16
@@ -1,6 +1,13 @@
|
||||
import { ModeListener } from './../listeners/general/ModeListener';
|
||||
import { PagesListener } from './../listeners/dashboard';
|
||||
import { ArticleHelper, CustomScript, Logger, Settings } from '.';
|
||||
import {
|
||||
ArticleHelper,
|
||||
CustomScript,
|
||||
Logger,
|
||||
Settings,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders
|
||||
} from '.';
|
||||
import {
|
||||
DefaultFieldValues,
|
||||
EXTENSION_NAME,
|
||||
@@ -26,7 +33,6 @@ import { Questions } from './Questions';
|
||||
import { Notifications } from './Notifications';
|
||||
import { DEFAULT_CONTENT_TYPE_NAME } from '../constants/ContentType';
|
||||
import { Telemetry } from './Telemetry';
|
||||
import { processKnownPlaceholders } from './PlaceholderHelper';
|
||||
import { basename } from 'path';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { encodeEmoji, existsAsync, fieldWhenClause, writeFileAsync } from '../utils';
|
||||
@@ -299,17 +305,17 @@ export class ContentType {
|
||||
|
||||
Telemetry.send(TelemetryEvent.addMissingFields);
|
||||
|
||||
const content = ArticleHelper.getCurrent();
|
||||
const article = ArticleHelper.getCurrent();
|
||||
|
||||
if (!content || !content.data) {
|
||||
if (!article || !article.data) {
|
||||
Notifications.warning(
|
||||
l10n.t(LocalizationKey.helpersContentTypeAddMissingFieldsNoFrontMatterWarning)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(content);
|
||||
const updatedFields = ContentType.generateFields(content.data, contentType.fields);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const updatedFields = ContentType.generateFields(article.data, contentType.fields);
|
||||
|
||||
const contentTypes = ContentType.getAll() || [];
|
||||
const index = contentTypes.findIndex((ct) => ct.name === contentType.name);
|
||||
@@ -927,7 +933,8 @@ export class ContentType {
|
||||
titleValue,
|
||||
templateData?.data || {},
|
||||
newFilePath,
|
||||
!!contentType.clearEmpty
|
||||
!!contentType.clearEmpty,
|
||||
contentType
|
||||
);
|
||||
|
||||
const article: ParsedFrontMatter = {
|
||||
@@ -982,6 +989,7 @@ export class ContentType {
|
||||
data: any,
|
||||
filePath: string,
|
||||
clearEmpty: boolean,
|
||||
contentType: IContentType,
|
||||
isRoot: boolean = true
|
||||
): Promise<any> {
|
||||
if (obj.fields) {
|
||||
@@ -995,9 +1003,13 @@ export class ContentType {
|
||||
|
||||
if (field.name === 'title') {
|
||||
if (field.default) {
|
||||
data[field.name] = processKnownPlaceholders(
|
||||
field.default,
|
||||
titleValue,
|
||||
data[field.name] = processArticlePlaceholdersFromData(
|
||||
field.default as string,
|
||||
data,
|
||||
contentType
|
||||
);
|
||||
data[field.name] = processTimePlaceholders(
|
||||
data[field.name],
|
||||
field.dateFormat || dateFormat
|
||||
);
|
||||
data[field.name] = await ArticleHelper.processCustomPlaceholders(
|
||||
@@ -1018,6 +1030,7 @@ export class ContentType {
|
||||
{},
|
||||
filePath,
|
||||
clearEmpty,
|
||||
contentType,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -1026,18 +1039,33 @@ export class ContentType {
|
||||
}
|
||||
} else {
|
||||
const defaultValue = field.default;
|
||||
console.log(field.name, defaultValue, Array.isArray(defaultValue));
|
||||
|
||||
if (typeof defaultValue === 'string') {
|
||||
data[field.name] = processKnownPlaceholders(
|
||||
data[field.name] = await ContentType.processFieldPlaceholders(
|
||||
defaultValue,
|
||||
titleValue,
|
||||
field.dateFormat || dateFormat
|
||||
);
|
||||
data[field.name] = await ArticleHelper.processCustomPlaceholders(
|
||||
data[field.name],
|
||||
data,
|
||||
contentType,
|
||||
field.dateFormat || dateFormat,
|
||||
titleValue,
|
||||
filePath
|
||||
);
|
||||
} else if (defaultValue && Array.isArray(defaultValue)) {
|
||||
let defaultValues = [];
|
||||
for (let value of defaultValue as string[]) {
|
||||
if (typeof value === 'string') {
|
||||
value = await ContentType.processFieldPlaceholders(
|
||||
value,
|
||||
data,
|
||||
contentType,
|
||||
field.dateFormat || dateFormat,
|
||||
titleValue,
|
||||
filePath
|
||||
);
|
||||
}
|
||||
defaultValues.push(value);
|
||||
}
|
||||
data[field.name] = defaultValues;
|
||||
} else if (typeof defaultValue !== 'undefined') {
|
||||
data[field.name] = defaultValue;
|
||||
} else {
|
||||
@@ -1082,6 +1110,7 @@ export class ContentType {
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
case 'slug':
|
||||
case 'image':
|
||||
case 'file':
|
||||
default:
|
||||
@@ -1100,6 +1129,32 @@ export class ContentType {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the field placeholders in the given value.
|
||||
*
|
||||
* @param defaultValue - The default value for the field.
|
||||
* @param data - The data object containing the field values.
|
||||
* @param contentType - The content type object.
|
||||
* @param dateFormat - The date format string.
|
||||
* @param title - The title string.
|
||||
* @param filePath - The file path string.
|
||||
* @returns The processed value with field placeholders replaced.
|
||||
*/
|
||||
private static async processFieldPlaceholders(
|
||||
defaultValue: string,
|
||||
data: any,
|
||||
contentType: IContentType,
|
||||
dateFormat: string,
|
||||
title: string,
|
||||
filePath: string
|
||||
) {
|
||||
let value = processArticlePlaceholdersFromData(defaultValue, data, contentType);
|
||||
value = processTimePlaceholders(value, dateFormat);
|
||||
value = await ArticleHelper.processCustomPlaceholders(value, title, filePath);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the content type feature is enabled
|
||||
* @returns
|
||||
|
||||
@@ -88,16 +88,12 @@ export class DashboardSettings {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isInitialized = await Project.isInitialized();
|
||||
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
|
||||
const pagination = Settings.get<boolean | number>(SETTING_DASHBOARD_CONTENT_PAGINATION);
|
||||
|
||||
const settings = {
|
||||
projects: Settings.getProjects(),
|
||||
project: Settings.getProject(),
|
||||
git: {
|
||||
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
|
||||
actions: gitActions || false
|
||||
},
|
||||
git: await GitListener.getSettings(),
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: Folders.getStaticFolderRelativePath(),
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
SETTING_SLUG_UPDATE_FILE_NAME,
|
||||
SETTING_TAXONOMY_CUSTOM,
|
||||
SETTING_TAXONOMY_FIELD_GROUPS,
|
||||
SETTING_GIT_ENABLED,
|
||||
SETTING_SEO_TITLE_FIELD
|
||||
} from '../constants';
|
||||
import { GitListener } from '../listeners/general';
|
||||
@@ -49,14 +48,9 @@ import { Folders } from '../commands';
|
||||
|
||||
export class PanelSettings {
|
||||
public static async get(): Promise<IPanelSettings> {
|
||||
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
|
||||
|
||||
return {
|
||||
aiEnabled: Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED) || false,
|
||||
git: {
|
||||
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
|
||||
actions: gitActions || false
|
||||
},
|
||||
git: await GitListener.getSettings(),
|
||||
seo: {
|
||||
title: (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1,
|
||||
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { stopWords, charMap } from '../constants';
|
||||
import { Settings } from '.';
|
||||
import { stopWords, charMap, SETTING_DATE_FORMAT, SETTING_SLUG_TEMPLATE } from '../constants';
|
||||
import { processTimePlaceholders, processFmPlaceholders } from '.';
|
||||
|
||||
export class SlugHelper {
|
||||
/**
|
||||
@@ -6,13 +8,41 @@ export class SlugHelper {
|
||||
*
|
||||
* @param articleTitle
|
||||
*/
|
||||
public static createSlug(articleTitle: string): string | null {
|
||||
public static createSlug(
|
||||
articleTitle: string,
|
||||
articleData: { [key: string]: any },
|
||||
slugTemplate?: string
|
||||
): string | null {
|
||||
if (!articleTitle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove punctuation from input string, and split it into words.
|
||||
let cleanTitle = this.removePunctuation(articleTitle).trim();
|
||||
if (!slugTemplate) {
|
||||
slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE) || undefined;
|
||||
}
|
||||
|
||||
if (slugTemplate) {
|
||||
if (slugTemplate.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
slugTemplate = slugTemplate.replace(regex, SlugHelper.slugify(articleTitle));
|
||||
}
|
||||
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
articleTitle = processTimePlaceholders(slugTemplate, dateFormat);
|
||||
articleTitle = processFmPlaceholders(articleTitle, articleData);
|
||||
return articleTitle;
|
||||
}
|
||||
|
||||
return SlugHelper.slugify(articleTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a title into a slug by removing punctuation, stop words, and replacing characters.
|
||||
* @param title - The title to be slugified.
|
||||
* @returns The slugified version of the title.
|
||||
*/
|
||||
public static slugify(title: string): string {
|
||||
let cleanTitle = this.removePunctuation(title).trim();
|
||||
if (cleanTitle) {
|
||||
cleanTitle = cleanTitle.toLowerCase();
|
||||
// Split into words
|
||||
@@ -23,8 +53,7 @@ export class SlugHelper {
|
||||
cleanTitle = this.replaceCharacters(cleanTitle);
|
||||
return cleanTitle;
|
||||
}
|
||||
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,6 @@ export * from './MediaHelpers';
|
||||
export * from './MediaLibrary';
|
||||
export * from './Notifications';
|
||||
export * from './PanelSettings';
|
||||
export * from './PlaceholderHelper';
|
||||
export * from './Questions';
|
||||
export * from './Sanitize';
|
||||
export * from './SeoHelper';
|
||||
@@ -32,5 +31,7 @@ export * from './getTaxonomyField';
|
||||
export * from './isValidFile';
|
||||
export * from './openFileInEditor';
|
||||
export * from './parseWinPath';
|
||||
export * from './processArticlePlaceholders';
|
||||
export * from './processFmPlaceholders';
|
||||
export * from './processPathPlaceholders';
|
||||
export * from './processTimePlaceholders';
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ContentType } from '../models';
|
||||
import { ArticleHelper } from './ArticleHelper';
|
||||
import { SlugHelper } from './SlugHelper';
|
||||
|
||||
export const processArticlePlaceholdersFromData = (
|
||||
value: string,
|
||||
data: { [key: string]: any },
|
||||
contentType: ContentType
|
||||
): string => {
|
||||
if (value.includes('{{title}}') && data.title) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, data.title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}')) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(
|
||||
regex,
|
||||
SlugHelper.createSlug(data.title || '', data, contentType.slugTemplate) || ''
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const processArticlePlaceholdersFromPath = async (
|
||||
value: string,
|
||||
filePath: string
|
||||
): Promise<string> => {
|
||||
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
if (!article) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, article.data.title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}') && filePath) {
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
if (contentType) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(
|
||||
regex,
|
||||
SlugHelper.createSlug(article.data.title || '', article.data, contentType.slugTemplate) ||
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export const processFmPlaceholders = (value: string, fmData: any) => {
|
||||
export const processFmPlaceholders = (value: string, fmData: { [key: string]: any }) => {
|
||||
// Example: {{fm.date}} or {{fm.date | dateFormat 'DD.MM.YYYY'}}
|
||||
if (value && value.includes('{{fm.')) {
|
||||
const regex = /{{fm.[^}]*}}/g;
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from './DateHelper';
|
||||
import { SlugHelper } from './SlugHelper';
|
||||
|
||||
/**
|
||||
* Replace the known placeholders
|
||||
* Replace the time placeholders
|
||||
* @param value
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
export const processKnownPlaceholders = (
|
||||
value: string,
|
||||
title: string | undefined,
|
||||
dateFormat: string
|
||||
) => {
|
||||
export const processTimePlaceholders = (value: string, dateFormat?: string) => {
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}')) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(regex, SlugHelper.createSlug(title || '') || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{now}}')) {
|
||||
const regex = new RegExp('{{now}}', 'g');
|
||||
|
||||
Reference in New Issue
Block a user