mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-07-03 16:31:32 +02:00
#473 - Allow setting the SEO title name
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#473](https://github.com/estruyf/vscode-front-matter/issues/473): Allow setting the SEO title name with the `frontMatter.taxonomy.seoTitleField` setting
|
||||
- [#474](https://github.com/estruyf/vscode-front-matter/issues/474): Allow to define the file prefix on content types
|
||||
- [#484](https://github.com/estruyf/vscode-front-matter/issues/484): Support for overriding scripts per environment type
|
||||
- [#494](https://github.com/estruyf/vscode-front-matter/issues/494): Support for external image URLs in previews
|
||||
|
||||
@@ -1492,6 +1492,12 @@
|
||||
"markdownDescription": "Specifies the optimal slug length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seoSlugLength)",
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.seoTitleField": {
|
||||
"type": "string",
|
||||
"default": "title",
|
||||
"markdownDescription": "Specifies the name of the SEO title field for your page. Default is 'title'. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seotitlefield)",
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.seoTitleLength": {
|
||||
"type": "number",
|
||||
"default": 60,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
NOTIFICATION_TYPE,
|
||||
SETTING_SEO_DESCRIPTION_FIELD,
|
||||
SETTING_SEO_DESCRIPTION_LENGTH,
|
||||
SETTING_SEO_TITLE_FIELD,
|
||||
SETTING_SEO_TITLE_LENGTH
|
||||
} from './../constants';
|
||||
import * as vscode from 'vscode';
|
||||
@@ -59,15 +60,17 @@ export class StatusListener {
|
||||
// Retrieve the SEO config properties
|
||||
const titleLength = (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1;
|
||||
const descLength = (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1;
|
||||
const fieldName =
|
||||
const titleField =
|
||||
(Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
|
||||
const descriptionField =
|
||||
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
|
||||
|
||||
if (article.data.title && titleLength > -1) {
|
||||
SeoHelper.checkLength(editor, collection, article, 'title', titleLength);
|
||||
if (article.data[titleField] && titleLength > -1) {
|
||||
SeoHelper.checkLength(editor, collection, article, titleField, titleLength);
|
||||
}
|
||||
|
||||
if (article.data[fieldName] && descLength > -1) {
|
||||
SeoHelper.checkLength(editor, collection, article, fieldName, descLength);
|
||||
if (article.data[descriptionField] && descLength > -1) {
|
||||
SeoHelper.checkLength(editor, collection, article, descriptionField, descLength);
|
||||
}
|
||||
|
||||
// Check the required fields
|
||||
|
||||
@@ -2,5 +2,6 @@ export const DefaultFields = {
|
||||
PublishingDate: `date`,
|
||||
LastModified: `lastmod`,
|
||||
Description: `description`,
|
||||
Title: `title`,
|
||||
Slug: `slug`
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ export const SETTING_REMOVE_QUOTES = 'taxonomy.noPropertyValueQuotes';
|
||||
|
||||
export const SETTING_FRONTMATTER_TYPE = 'taxonomy.frontMatterType';
|
||||
|
||||
export const SETTING_SEO_TITLE_FIELD = 'taxonomy.seoTitleField';
|
||||
export const SETTING_SEO_TITLE_LENGTH = 'taxonomy.seoTitleLength';
|
||||
export const SETTING_SEO_SLUG_LENGTH = 'taxonomy.seoSlugLength';
|
||||
export const SETTING_SEO_DESCRIPTION_LENGTH = 'taxonomy.seoDescriptionLength';
|
||||
|
||||
@@ -29,7 +29,8 @@ import {
|
||||
SETTING_TAXONOMY_CUSTOM,
|
||||
SETTING_TAXONOMY_FIELD_GROUPS,
|
||||
SETTING_TAXONOMY_TAGS,
|
||||
SETTING_GIT_ENABLED
|
||||
SETTING_GIT_ENABLED,
|
||||
SETTING_SEO_TITLE_FIELD
|
||||
} from '../constants';
|
||||
import { GitListener } from '../listeners/general';
|
||||
import {
|
||||
@@ -55,6 +56,7 @@ export class PanelSettings {
|
||||
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
|
||||
description: (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1,
|
||||
content: (Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number) || -1,
|
||||
titleField: (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title,
|
||||
descriptionField:
|
||||
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description
|
||||
},
|
||||
|
||||
@@ -136,6 +136,7 @@ export interface SEO {
|
||||
slug: number;
|
||||
description: number;
|
||||
content: number;
|
||||
titleField: string;
|
||||
descriptionField: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
);
|
||||
} else if (field.type === 'string') {
|
||||
let limit = -1;
|
||||
if (field.name === 'title') {
|
||||
if (field.name === settings.seo.titleField) {
|
||||
limit = settings?.seo.title;
|
||||
} else if (field.name === settings.seo.descriptionField) {
|
||||
limit = settings?.seo.description;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { SEO } from '../../models/PanelSettings';
|
||||
import { TagType } from '../TagType';
|
||||
import { ArticleDetails } from './ArticleDetails';
|
||||
@@ -32,10 +33,10 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
}, 10);
|
||||
}).current;
|
||||
|
||||
const { descriptionField } = seo;
|
||||
const { descriptionField, titleField } = seo;
|
||||
|
||||
// Workaround for lit components not updating render
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
let height = 0;
|
||||
|
||||
@@ -47,7 +48,7 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
tableRef.current.style.height = `${height}px`;
|
||||
}
|
||||
}, 10);
|
||||
}, [title, data[descriptionField], data?.articleDetails?.wordCount]);
|
||||
}, [title, data[titleField], data[descriptionField], data?.articleDetails?.wordCount]);
|
||||
|
||||
const renderContent = () => {
|
||||
if (!isOpen) {
|
||||
@@ -66,14 +67,15 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
<VsTableHeaderCell className={`table__cell`}>Valid</VsTableHeaderCell>
|
||||
</VsTableHeader>
|
||||
<VsTableBody slot="body">
|
||||
{title && seo.title > 0 && (
|
||||
{data[titleField] && seo.title > 0 && (
|
||||
<SeoFieldInfo
|
||||
title={`title`}
|
||||
value={title.length}
|
||||
title={titleField}
|
||||
value={data[titleField].length}
|
||||
recommendation={`${seo.title} chars`}
|
||||
isValid={title.length <= seo.title}
|
||||
isValid={data[titleField].length <= seo.title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{slug && seo.slug > 0 && (
|
||||
<SeoFieldInfo
|
||||
title={`slug`}
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
DefaultFields,
|
||||
DEFAULT_CONTENT_TYPE_NAME,
|
||||
ExtensionState,
|
||||
SETTING_SEO_DESCRIPTION_FIELD
|
||||
SETTING_SEO_DESCRIPTION_FIELD,
|
||||
SETTING_SEO_TITLE_FIELD
|
||||
} from '../constants';
|
||||
import { Page } from '../dashboardWebView/models';
|
||||
import {
|
||||
@@ -155,10 +156,12 @@ export class PagesParser {
|
||||
folderTitle: string
|
||||
): Promise<Page | undefined> {
|
||||
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
const articleTitle = article?.data.title || fileName;
|
||||
|
||||
if (article?.data) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
|
||||
|
||||
const descriptionField =
|
||||
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
|
||||
|
||||
@@ -175,7 +178,7 @@ export class PagesParser {
|
||||
|
||||
const staticFolder = Folders.getStaticFolderRelativePath();
|
||||
|
||||
let escapedTitle = articleTitle;
|
||||
let escapedTitle = article?.data[titleField] || fileName;
|
||||
if (escapedTitle && typeof escapedTitle !== 'string') {
|
||||
escapedTitle = '<invalid title>';
|
||||
}
|
||||
@@ -205,10 +208,10 @@ export class PagesParser {
|
||||
fmBody: article?.content || '',
|
||||
// Make sure these are always set
|
||||
title: escapedTitle,
|
||||
description: escapedDescription,
|
||||
slug: article?.data.slug,
|
||||
date: article?.data[dateField] || '',
|
||||
draft: article?.data.draft,
|
||||
description: escapedDescription
|
||||
draft: article?.data.draft
|
||||
};
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article.data);
|
||||
|
||||
Reference in New Issue
Block a user