From 69a18bdceaed9cac7127fdf85993d0704a7b8d77 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 6 Sep 2023 12:06:19 +0200 Subject: [PATCH] #570 - Clear empty values --- CHANGELOG.md | 1 + package.json | 280 +++++++++++++--------------- package.nls.json | 3 +- src/helpers/ContentType.ts | 41 +++- src/listeners/panel/DataListener.ts | 21 +++ src/models/PanelSettings.ts | 1 + src/parsers/ParserEngines.ts | 5 +- 7 files changed, 187 insertions(+), 165 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b68ef32e..33c43fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### ✨ New features +- [#570](https://github.com/estruyf/vscode-front-matter/issues/570): Clear empty values on content creation and editing - [#645](https://github.com/estruyf/vscode-front-matter/issues/645): French localization added (thanks to [Clément Barbaza](https://github.com/cba85)) ### 🎨 Enhancements diff --git a/package.json b/package.json index eb97a624..5885fe44 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "color": "#0e131f", "theme": "dark" }, - "badges": [ - { + "badges": [{ "description": "version", "url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square", "href": "https://github.com/estruyf/vscode-front-matter" @@ -62,8 +61,7 @@ "**/.frontmatter/config/*.json": "jsonc" } }, - "keybindings": [ - { + "keybindings": [{ "command": "frontMatter.dashboard", "key": "alt+d" }, @@ -81,24 +79,20 @@ } ], "viewsContainers": { - "activitybar": [ - { - "id": "frontmatter-explorer", - "title": "Front Matter", - "icon": "assets/frontmatter-short-min.svg" - } - ] + "activitybar": [{ + "id": "frontmatter-explorer", + "title": "Front Matter", + "icon": "assets/frontmatter-short-min.svg" + }] }, "views": { - "frontmatter-explorer": [ - { - "id": "frontMatter.explorer", - "name": "Front Matter", - "icon": "assets/frontmatter-short-min.svg", - "contextualTitle": "Front Matter", - "type": "webview" - } - ] + "frontmatter-explorer": [{ + "id": "frontMatter.explorer", + "name": "Front Matter", + "icon": "assets/frontmatter-short-min.svg", + "contextualTitle": "Front Matter", + "type": "webview" + }] }, "configuration": { "$id": "#globalconfiguration", @@ -162,8 +156,7 @@ "frontMatter.content.defaultFileType": { "type": "string", "default": "md", - "oneOf": [ - { + "oneOf": [{ "enum": [ "md", "mdx" @@ -179,8 +172,7 @@ "frontMatter.content.defaultSorting": { "type": "string", "default": "", - "oneOf": [ - { + "oneOf": [{ "enum": [ "LastModifiedAsc", "LastModifiedDesc", @@ -527,8 +519,7 @@ "command": { "$id": "#scriptCommand", "type": "string", - "anyOf": [ - { + "anyOf": [{ "enum": [ "node", "bash", @@ -728,8 +719,7 @@ "title", "file" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -783,8 +773,7 @@ "id", "path" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -1152,8 +1141,7 @@ "default": "", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1273,6 +1261,11 @@ "default": false, "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.required.description%" }, + "clearEmpty": { + "type": "boolean", + "default": false, + "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description%" + }, "customType": { "type": "string", "default": "", @@ -1342,8 +1335,7 @@ "type", "name" ], - "allOf": [ - { + "allOf": [{ "if": { "properties": { "type": { @@ -1529,51 +1521,48 @@ "fields" ] }, - "default": [ - { - "name": "default", - "pageBundle": false, - "fields": [ - { - "title": "Title", - "name": "title", - "type": "string" - }, - { - "title": "Description", - "name": "description", - "type": "string" - }, - { - "title": "Publishing date", - "name": "date", - "type": "datetime", - "default": "{{now}}", - "isPublishDate": true - }, - { - "title": "Content preview", - "name": "preview", - "type": "image" - }, - { - "title": "Is in draft", - "name": "draft", - "type": "boolean" - }, - { - "title": "Tags", - "name": "tags", - "type": "tags" - }, - { - "title": "Categories", - "name": "categories", - "type": "categories" - } - ] - } - ], + "default": [{ + "name": "default", + "pageBundle": false, + "fields": [{ + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Description", + "name": "description", + "type": "string" + }, + { + "title": "Publishing date", + "name": "date", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "Content preview", + "name": "preview", + "type": "image" + }, + { + "title": "Is in draft", + "name": "draft", + "type": "boolean" + }, + { + "title": "Tags", + "name": "tags", + "type": "tags" + }, + { + "title": "Categories", + "name": "categories", + "type": "categories" + } + ] + }], "scope": "Taxonomy" }, "frontMatter.taxonomy.customTaxonomy": { @@ -1586,8 +1575,7 @@ "type": "string", "description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1769,8 +1757,7 @@ } } }, - "commands": [ - { + "commands": [{ "command": "frontMatter.project.switch", "title": "%command.frontMatter.project.switch%", "category": "Front Matter", @@ -2099,15 +2086,12 @@ "category": "Front Matter" } ], - "submenus": [ - { - "id": "frontmatter.submenu", - "label": "Front Matter" - } - ], + "submenus": [{ + "id": "frontmatter.submenu", + "label": "Front Matter" + }], "menus": { - "editor/title": [ - { + "editor/title": [{ "command": "frontMatter.markup.heading", "group": "navigation@-133", "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" @@ -2188,14 +2172,11 @@ "when": "resourceFilename == 'frontmatter.json'" } ], - "explorer/context": [ - { - "submenu": "frontmatter.submenu", - "group": "frontmatter@1" - } - ], - "frontmatter.submenu": [ - { + "explorer/context": [{ + "submenu": "frontmatter.submenu", + "group": "frontmatter@1" + }], + "frontmatter.submenu": [{ "command": "frontMatter.createFromTemplate", "when": "explorerResourceIsFolder", "group": "frontmatter@1" @@ -2211,8 +2192,7 @@ "group": "frontmatter@3" } ], - "commandPalette": [ - { + "commandPalette": [{ "command": "frontMatter.init", "when": "frontMatterCanInit" }, @@ -2357,8 +2337,7 @@ "when": "frontMatter:file:isValid == true" } ], - "view/title": [ - { + "view/title": [{ "command": "frontMatter.chatbot", "group": "navigation@0", "when": "view == frontMatter.explorer" @@ -2385,57 +2364,52 @@ } ] }, - "grammars": [ - { - "path": "./syntaxes/hugo.tmLanguage.json", - "scopeName": "frontmatter.markdown.hugo", - "injectTo": [ - "text.html.markdown" - ] - } - ], - "walkthroughs": [ - { - "id": "frontmatter.welcome", - "title": "Get started with Front Matter", - "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", - "steps": [ - { - "id": "frontmatter.welcome.init", - "title": "Get started", - "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", - "media": { - "markdown": "assets/walkthrough/get-started.md" - }, - "completionEvents": [ - "onContext:frontMatterInitialized" - ] + "grammars": [{ + "path": "./syntaxes/hugo.tmLanguage.json", + "scopeName": "frontmatter.markdown.hugo", + "injectTo": [ + "text.html.markdown" + ] + }], + "walkthroughs": [{ + "id": "frontmatter.welcome", + "title": "Get started with Front Matter", + "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", + "steps": [{ + "id": "frontmatter.welcome.init", + "title": "Get started", + "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", + "media": { + "markdown": "assets/walkthrough/get-started.md" }, - { - "id": "frontmatter.welcome.documentation", - "title": "Documentation", - "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", - "media": { - "markdown": "assets/walkthrough/documentation.md" - }, - "completionEvents": [ - "onLink:https://frontmatter.codes/docs" - ] + "completionEvents": [ + "onContext:frontMatterInitialized" + ] + }, + { + "id": "frontmatter.welcome.documentation", + "title": "Documentation", + "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", + "media": { + "markdown": "assets/walkthrough/documentation.md" }, - { - "id": "frontmatter.welcome.supporter", - "title": "Support the project", - "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", - "media": { - "markdown": "assets/walkthrough/support-the-project.md" - }, - "completionEvents": [ - "onLink:https://github.com/sponsors/estruyf" - ] - } - ] - } - ] + "completionEvents": [ + "onLink:https://frontmatter.codes/docs" + ] + }, + { + "id": "frontmatter.welcome.supporter", + "title": "Support the project", + "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", + "media": { + "markdown": "assets/walkthrough/support-the-project.md" + }, + "completionEvents": [ + "onLink:https://github.com/sponsors/estruyf" + ] + } + ] + }] }, "scripts": { "dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*", @@ -2569,4 +2543,4 @@ "vsce": { "dependencies": false } -} +} \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index 41fb9876..a972a490 100644 --- a/package.nls.json +++ b/package.nls.json @@ -239,5 +239,6 @@ "setting.frontMatter.dashboard.mediaSnippet.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please define your media snippet in the `frontMatter.content.snippet` setting.", "setting.frontMatter.taxonomy.dateField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isPublishDate` settings instead in your content types date fields.", "setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isModifiedDate` settings instead in your content types date fields.", - "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "Specify the name of the custom field type to use." + "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "Specify the name of the custom field type to use.", + "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "Specify if the empty values should be cleared." } \ No newline at end of file diff --git a/src/helpers/ContentType.ts b/src/helpers/ContentType.ts index 5ea36652..42e44a8b 100644 --- a/src/helpers/ContentType.ts +++ b/src/helpers/ContentType.ts @@ -737,7 +737,8 @@ export class ContentType { contentType, titleValue, templateData?.data || {}, - newFilePath + newFilePath, + !!contentType.clearEmpty ); data = ArticleHelper.updateDates(Object.assign({}, data)); @@ -785,6 +786,7 @@ export class ContentType { titleValue: string, data: any, filePath: string, + clearEmpty: boolean, isRoot: boolean = true ): Promise { if (obj.fields) { @@ -805,12 +807,23 @@ export class ContentType { ); } else if (isRoot) { data[field.name] = titleValue; - } else { + } else if (!clearEmpty) { data[field.name] = ''; } } else { if (field.type === 'fields') { - data[field.name] = await this.processFields(field, titleValue, {}, filePath, false); + data[field.name] = await this.processFields( + field, + titleValue, + {}, + filePath, + clearEmpty, + false + ); + + if (clearEmpty && Object.keys(data[field.name]).length === 0) { + delete data[field.name]; + } } else { const defaultValue = field.default; @@ -841,30 +854,40 @@ export class ContentType { case 'choice': if (field.multiple) { data[field.name] = []; - } else { + } else if (!clearEmpty) { data[field.name] = ''; } break; case 'boolean': - data[field.name] = false; + if (!clearEmpty) { + data[field.name] = false; + } break; case 'number': - data[field.name] = 0; + if (!clearEmpty) { + data[field.name] = 0; + } break; case 'datetime': - data[field.name] = null; + if (!clearEmpty) { + data[field.name] = null; + } break; case 'list': case 'tags': case 'categories': case 'taxonomy': - data[field.name] = []; + if (!clearEmpty) { + data[field.name] = []; + } break; case 'string': case 'image': case 'file': default: - data[field.name] = ''; + if (!clearEmpty) { + data[field.name] = ''; + } break; } } diff --git a/src/listeners/panel/DataListener.ts b/src/listeners/panel/DataListener.ts index f9b4a121..a4f24e38 100644 --- a/src/listeners/panel/DataListener.ts +++ b/src/listeners/panel/DataListener.ts @@ -232,6 +232,8 @@ export class DataListener extends BaseListener { return; } + const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title; + const editor = window.activeTextEditor; let article; @@ -248,6 +250,10 @@ export class DataListener extends BaseListener { const contentType = ArticleHelper.getContentType(article.data); + if (!value && field !== titleField && contentType.clearEmpty) { + value = undefined; + } + const dateFields = ContentType.findFieldsByTypeDeep(contentType.fields, 'datetime'); const imageFields = ContentType.findFieldsByTypeDeep(contentType.fields, 'image'); const fileFields = ContentType.findFieldsByTypeDeep(contentType.fields, 'file'); @@ -263,6 +269,7 @@ export class DataListener extends BaseListener { } }); + // Check multi-image fields const multiImageFieldsArray = imageFields.find((f: Field[]) => { const lastField = f?.[f.length - 1]; if (lastField) { @@ -270,6 +277,7 @@ export class DataListener extends BaseListener { } }); + // Check multi-file fields const multiFileFieldsArray = fileFields.find((f: Field[]) => { const lastField = f?.[f.length - 1]; if (lastField) { @@ -277,6 +285,7 @@ export class DataListener extends BaseListener { } }); + // Check date fields if (dateFieldsArray && dateFieldsArray.length > 0) { for (const dateField of dateFieldsArray) { if (field === dateField.name && value) { @@ -322,6 +331,18 @@ export class DataListener extends BaseListener { } } + // Clear the field if it is empty + if ( + value === undefined || + (value instanceof Array && value.length === 0 && contentType.clearEmpty) + ) { + delete parentObj[field]; + } + + if (Object.keys(parentObj).length === 0 && field !== titleField && contentType.clearEmpty) { + delete article.data[parents![0]]; + } + if (editor) { ArticleHelper.update(editor, article); } else if (filePath) { diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 5fdd5752..16939371 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -53,6 +53,7 @@ export interface ContentType { template?: string; postScript?: string; filePrefix?: string; + clearEmpty?: boolean; } export type FieldType = diff --git a/src/parsers/ParserEngines.ts b/src/parsers/ParserEngines.ts index 0364aeb9..9288f2a9 100644 --- a/src/parsers/ParserEngines.ts +++ b/src/parsers/ParserEngines.ts @@ -68,7 +68,7 @@ export const Engines = { // Check if there are values to remove for (const key in docYaml.toJSON()) { - if (typeof obj[key] === undefined) { + if (typeof obj[key] === 'undefined') { docYaml.delete(key); } } @@ -77,7 +77,8 @@ export const Engines = { return yaml.stringify(updatedValue, { lineWidth: 5000, - defaultStringType: 'PLAIN' + defaultStringType: 'PLAIN', + keepUndefined: false }); } }