Compare commits

...

8 Commits

Author SHA1 Message Date
Elio Struyf 1b5909b706 chore: update CHANGELOG for version 10.11.0 and add fix for number fields in front matter
refactor: improve empty field handling in DataListener and DataBlockField components
2026-05-14 11:11:59 +02:00
Elio Struyf 34ac4952e0 refactor: streamline notification error and warning messages in ArticleHelper 2026-05-13 11:32:36 +02:00
Elio Struyf c0614ad228 Merge branch 'beta' of github.com:estruyf/vscode-front-matter into beta 2026-05-13 11:09:43 +02:00
Elio Struyf 6c9de39501 10.11.0 2026-05-12 09:08:21 +02:00
Elio Struyf 59425d6fc7 chore: update CHANGELOG for version 10.11.0 and fix duplicate entry in 10.10.1 2026-05-12 09:05:05 +02:00
Elio Struyf 9b81bbaafe Merge pull request #1030 from danil-instacart/feat/slug-separator
feat: add frontMatter.file.slugSeparator setting
2026-05-12 09:00:19 +02:00
Danil Semelenov 43190099e5 feat: add frontMatter.file.slugSeparator setting
Allows users to configure the word separator used when generating slugs
and file names from titles. Defaults to `-` (hyphen) to preserve existing
behavior. Set to `_` for underscore-separated file names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 14:16:38 -04:00
Elio Struyf 9dfa1d8775 fix: standardize quotes and formatting in release-beta.yml 2026-05-05 09:27:24 +02:00
10 changed files with 72 additions and 43 deletions
+22 -11
View File
@@ -6,13 +6,14 @@ on:
workflow_dispatch:
env:
PACKAGE_NAME: 'fm-localized'
MS_URL: 'https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta'
VSX_URL: 'https://open-vsx.org/extension/eliostruyf/vscode-front-matter-beta'
PACKAGE_NAME: "fm-localized"
MS_URL: "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-\
front-matter-beta"
VSX_URL: "https://open-vsx.org/extension/eliostruyf/vscode-front-matter-beta"
jobs:
localization:
name: 'Localization'
name: "Localization"
runs-on: ubuntu-latest
steps:
@@ -27,11 +28,11 @@ jobs:
PACKAGE_NAME: ${{ env.PACKAGE_NAME }}
release-ms:
name: 'Release to VSCode Marketplace'
name: "Release to VSCode Marketplace"
runs-on: ubuntu-latest
needs: localization
environment:
name: 'MS - BETA'
name: "MS - BETA"
url: ${{ env.MS_URL }}
steps:
@@ -43,7 +44,7 @@ jobs:
with:
node-version: 20
registry-url: https://registry.npmjs.org/
cache: 'npm'
cache: "npm"
- name: Install the dependencies
run: npm ci
@@ -52,14 +53,24 @@ jobs:
run: node scripts/beta-release.js $GITHUB_RUN_ID
- name: Publish
run: npx @vscode/vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
run: npx @vscode/vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl
https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
- name: Package VSIX
run: npx @vscode/vsce package
- name: Upload VSIX artifact
uses: actions/upload-artifact@v6
with:
name: vscode-demo-time-alpha.vsix
path: "*.vsix"
release-vsx:
name: 'Release to Open VSX'
name: "Release to Open VSX"
runs-on: ubuntu-latest
needs: localization
environment:
name: 'Open VSX - BETA'
name: "Open VSX - BETA"
url: ${{ env.VSX_URL }}
steps:
@@ -71,7 +82,7 @@ jobs:
with:
node-version: 20
registry-url: https://registry.npmjs.org/
cache: 'npm'
cache: "npm"
- name: Install the dependencies
run: npm ci
+11 -1
View File
@@ -1,12 +1,22 @@
# Change Log
## [10.11.0] - 2026-xx-xx
### 🎨 Enhancements
- [#1030](https://github.com/estruyf/vscode-front-matter/pull/1030): Add `frontMatter.file.slugSeparator` setting
### 🐞 Fixes
- Fix number fields not being saved to front matter when used inside block field groups
## [10.10.1] - 2026-04-23
### 🐞 Fixes
- Fix Git detection when Git is configured via VS Code `git.path` and not installed globally on the system
- [#1023](https://github.com/estruyf/vscode-front-matter/issues/1023): Fix validation errors for image, file, and keywords fields
- [#1024](https://github.com/estruyf/vscode-front-matter/issues/1024): Re-add the `frontMatter.copilot.enabled` setting to allow users to disable the GitHub Copilot integration
- Fix Git detection when Git is configured via VS Code `git.path` and not installed globally on the system
## [10.10.0] - 2026-04-03 - [Release notes](https://beta.frontmatter.codes/updates/v10.10.0)
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vscode-front-matter-beta",
"version": "10.10.1",
"version": "10.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-front-matter-beta",
"version": "10.10.1",
"version": "10.11.0",
"license": "MIT",
"devDependencies": {
"@actions/core": "^1.10.0",
+7 -1
View File
@@ -3,7 +3,7 @@
"displayName": "Front Matter CMS",
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...",
"icon": "assets/frontmatter-teal-128x128.png",
"version": "10.10.1",
"version": "10.11.0",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -965,6 +965,12 @@
"markdownDescription": "%setting.frontMatter.file.preserveCasing.markdownDescription%",
"scope": "File"
},
"frontMatter.file.slugSeparator": {
"type": "string",
"default": "-",
"markdownDescription": "%setting.frontMatter.file.slugSeparator.markdownDescription%",
"scope": "File"
},
"frontMatter.framework.id": {
"type": "string",
"default": "",
+1
View File
@@ -155,6 +155,7 @@
"setting.frontMatter.data.types.markdownDescription": "Specify the data types. These types can be used in for your data files. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.types) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.types%22%5D)",
"setting.frontMatter.data.types.items.properties.id.description": "Your unique ID you want to use for your data type.",
"setting.frontMatter.file.preserveCasing.markdownDescription": "Specify if you want to preserve the casing of your file names from the title. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.file.preservecasing) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.file.preservecasing%22%5D)",
"setting.frontMatter.file.slugSeparator.markdownDescription": "Specify the separator character used between words when generating slugs and file names from titles. Default is `-` (hyphen). Use `_` for underscores.",
"setting.frontMatter.framework.id.markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.id) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.id%22%5D)",
"setting.frontMatter.framework.startCommand.markdownDescription": "Specify the command you want to use to start your static site generator or framework. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.startcommand) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.startcommand%22%5D)",
"setting.frontMatter.git.enabled.markdownDescription": "Specify if you want to use the Git actions for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.enabled%22%5D)",
+1
View File
@@ -97,6 +97,7 @@ export const SETTING_DATA_FOLDERS = 'data.folders';
export const SETTING_DATA_TYPES = 'data.types';
export const SETTING_FILE_PRESERVE_CASING = 'file.preserveCasing';
export const SETTING_FILE_SLUG_SEPARATOR = 'file.slugSeparator';
export const SETTING_FRAMEWORK_ID = 'framework.id';
export const SETTING_FRAMEWORK_START = 'framework.startCommand';
+10 -23
View File
@@ -10,6 +10,7 @@ import {
SETTING_CONTENT_PLACEHOLDERS,
SETTING_CONTENT_SUPPORTED_FILETYPES,
SETTING_FILE_PRESERVE_CASING,
SETTING_FILE_SLUG_SEPARATOR,
SETTING_COMMA_SEPARATED_FIELDS,
SETTING_DATE_FIELD,
SETTING_DATE_FORMAT,
@@ -255,18 +256,14 @@ export class ArticleHelper {
const article = await ArticleHelper.getFrontMatterByPath(filePath);
if (!article || !article.data) {
Notifications.error(
l10n.t(LocalizationKey.commandsArticleRenameFileNotExistsError)
);
Notifications.error(l10n.t(LocalizationKey.commandsArticleRenameFileNotExistsError));
return;
}
const titleField = getTitleField();
const title: string = article.data[titleField];
if (!title) {
Notifications.warning(
l10n.t(LocalizationKey.commandsArticleSmartRenameUnableToGenerate)
);
Notifications.warning(l10n.t(LocalizationKey.commandsArticleSmartRenameUnableToGenerate));
return;
}
@@ -303,9 +300,7 @@ export class ArticleHelper {
const currentFolderName = parseFile(folderPath).base;
if (currentFolderName === newFileName) {
Notifications.info(
l10n.t(LocalizationKey.commandsArticleSmartRenameAlreadyInSync)
);
Notifications.info(l10n.t(LocalizationKey.commandsArticleSmartRenameAlreadyInSync));
return;
}
@@ -322,11 +317,7 @@ export class ArticleHelper {
});
Notifications.info(
l10n.t(
LocalizationKey.commandsArticleSmartRenameSuccess,
currentFolderName,
newFileName
)
l10n.t(LocalizationKey.commandsArticleSmartRenameSuccess, currentFolderName, newFileName)
);
} else {
// For regular files, rename the file
@@ -340,9 +331,7 @@ export class ArticleHelper {
}
if (parsed.base === newFileBase) {
Notifications.info(
l10n.t(LocalizationKey.commandsArticleSmartRenameAlreadyInSync)
);
Notifications.info(l10n.t(LocalizationKey.commandsArticleSmartRenameAlreadyInSync));
return;
}
@@ -367,11 +356,7 @@ export class ArticleHelper {
});
Notifications.info(
l10n.t(
LocalizationKey.commandsArticleSmartRenameSuccess,
parsed.base,
newFileBase
)
l10n.t(LocalizationKey.commandsArticleSmartRenameSuccess, parsed.base, newFileBase)
);
}
}
@@ -660,7 +645,9 @@ export class ArticleHelper {
*/
public static sanitize(value: string): string {
const preserveCasing = Settings.get(SETTING_FILE_PRESERVE_CASING) as boolean;
return sanitize((preserveCasing ? value : value.toLowerCase()).replace(/ /g, '-'));
const separator = (Settings.get(SETTING_FILE_SLUG_SEPARATOR) as string) || '-';
const sanitized = sanitize(preserveCasing ? value : value.toLowerCase());
return sanitized.replace(/ /g, separator);
}
/**
+5 -3
View File
@@ -1,5 +1,5 @@
import { parseWinPath, Settings } from '.';
import { stopWords, charMap, SETTING_DATE_FORMAT, SETTING_SLUG_TEMPLATE } from '../constants';
import { stopWords, charMap, SETTING_DATE_FORMAT, SETTING_SLUG_TEMPLATE, SETTING_FILE_SLUG_SEPARATOR } from '../constants';
import { processTimePlaceholders, processFmPlaceholders } from '.';
import { parse } from 'path';
@@ -26,7 +26,8 @@ export class SlugHelper {
if (typeof slugTemplate === 'string') {
if (slugTemplate.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
slugTemplate = slugTemplate.replace(regex, articleTitle.toLowerCase().replace(/\s/g, '-'));
const separator = (Settings.get(SETTING_FILE_SLUG_SEPARATOR) as string) || '-';
slugTemplate = slugTemplate.replace(regex, articleTitle.toLowerCase().replace(/\s/g, separator));
} else if (slugTemplate.includes('{{seoTitle}}')) {
const regex = new RegExp('{{seoTitle}}', 'g');
slugTemplate = slugTemplate.replace(regex, SlugHelper.slugify(articleTitle));
@@ -69,7 +70,8 @@ export class SlugHelper {
let words = cleanTitle.split(/\s/);
// Removing stop words
words = this.removeStopWords(words);
cleanTitle = words.join('-');
const separator = (Settings.get(SETTING_FILE_SLUG_SEPARATOR) as string) || '-';
cleanTitle = words.join(separator);
cleanTitle = this.replaceCharacters(cleanTitle);
return cleanTitle;
}
+5 -1
View File
@@ -479,7 +479,11 @@ export class DataListener extends BaseListener {
const contentType = await ArticleHelper.getContentType(article);
const sourceField = ContentType.findFieldByName(contentType.fields, field);
if (!value && field !== titleField && contentType.clearEmpty) {
if (
(value === undefined || value === null || value === '' || value === false) &&
field !== titleField &&
contentType.clearEmpty
) {
// Check if the draft or boolean field needs to be cleared
// This is only required when the default value is not set to true
if (sourceField && (sourceField.type === 'draft' || sourceField.type === 'boolean')) {
@@ -98,7 +98,14 @@ export const DataBlockField: React.FunctionComponent<IDataBlockFieldProps> = ({
// Remove the empty fields
Object.keys(data).forEach((key) => {
if (data[key] === undefined || data[key] === null || Object.keys(data[key]).length === 0) {
const currentValue = data[key];
const isObjectValue =
typeof currentValue === 'object' && currentValue !== null;
const isEmptyArray = Array.isArray(currentValue) && currentValue.length === 0;
const isEmptyObject =
isObjectValue && !Array.isArray(currentValue) && Object.keys(currentValue).length === 0;
if (currentValue === undefined || currentValue === null || isEmptyArray || isEmptyObject) {
delete data[key];
}
});