diff --git a/package.json b/package.json index 285beb13..b6529f63 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" @@ -70,8 +69,7 @@ "**/.frontmatter/config/*.json": "jsonc" } }, - "keybindings": [ - { + "keybindings": [{ "command": "frontMatter.dashboard", "key": "alt+d" }, @@ -95,23 +93,19 @@ } ], "viewsContainers": { - "activitybar": [ - { - "id": "frontmatter-explorer", - "title": "FM", - "icon": "$(fm-logo)" - } - ] + "activitybar": [{ + "id": "frontmatter-explorer", + "title": "FM", + "icon": "$(fm-logo)" + }] }, "views": { - "frontmatter-explorer": [ - { - "id": "frontMatter.explorer", - "name": "Front Matter", - "icon": "$(fm-logo)", - "type": "webview" - } - ] + "frontmatter-explorer": [{ + "id": "frontMatter.explorer", + "name": "Front Matter", + "icon": "$(fm-logo)", + "type": "webview" + }] }, "configuration": { "title": "%settings.configuration.title%", @@ -179,8 +173,7 @@ "frontMatter.content.defaultFileType": { "type": "string", "default": "md", - "oneOf": [ - { + "oneOf": [{ "enum": [ "md", "mdx" @@ -196,8 +189,7 @@ "frontMatter.content.defaultSorting": { "type": "string", "default": "", - "oneOf": [ - { + "oneOf": [{ "enum": [ "LastModifiedAsc", "LastModifiedDesc", @@ -298,6 +290,10 @@ "default": null, "description": "%setting.frontMatter.content.pageFolders.items.properties.previewPath.description%" }, + "trailingSlash": { + "type": "boolean", + "description": "%setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description%" + }, "filePrefix": { "type": [ "null", @@ -545,8 +541,7 @@ "categories" ], "markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%", - "items": [ - { + "items": [{ "type": "string", "enum": [ "contentFolders", @@ -619,8 +614,7 @@ "command": { "$id": "#scriptCommand", "type": "string", - "anyOf": [ - { + "anyOf": [{ "enum": [ "node", "bash", @@ -816,8 +810,7 @@ "title", "file" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -871,8 +864,7 @@ "id", "path" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -1113,29 +1105,26 @@ } } }, - "default": [ - { - "name": "default", - "fileTypes": null, - "fields": [ - { - "title": "Title", - "name": "title", - "type": "string" - }, - { - "title": "Caption", - "name": "caption", - "type": "string" - }, - { - "title": "Alt text", - "name": "alt", - "type": "string" - } - ] - } - ], + "default": [{ + "name": "default", + "fileTypes": null, + "fields": [{ + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Caption", + "name": "caption", + "type": "string" + }, + { + "title": "Alt text", + "name": "alt", + "type": "string" + } + ] + }], "scope": "Media" }, "frontMatter.media.supportedMimeTypes": { @@ -1186,6 +1175,12 @@ "markdownDescription": "%setting.frontMatter.preview.pathName.markdownDescription%", "scope": "Site preview" }, + "frontMatter.preview.trailingSlash": { + "type": "string", + "default": "", + "markdownDescription": "%setting.frontMatter.preview.trailingSlash.markdownDescription%", + "scope": "Site preview" + }, "frontMatter.site.baseURL": { "type": "string", "default": "", @@ -1365,8 +1360,7 @@ "default": "", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1560,8 +1554,7 @@ "type", "name" ], - "allOf": [ - { + "allOf": [{ "if": { "properties": { "type": { @@ -1732,6 +1725,10 @@ "default": null, "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description%" }, + "trailingSlash": { + "type": "boolean", + "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description%" + }, "slugTemplate": { "type": [ "null", @@ -1769,51 +1766,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": { @@ -1826,8 +1820,7 @@ "type": "string", "description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -2022,8 +2015,7 @@ } } }, - "commands": [ - { + "commands": [{ "command": "frontMatter.project.switch", "title": "%command.frontMatter.project.switch%", "category": "Front Matter", @@ -2349,21 +2341,16 @@ } } ], - "submenus": [ - { - "id": "frontmatter.submenu", - "label": "Front Matter" - } - ], + "submenus": [{ + "id": "frontmatter.submenu", + "label": "Front Matter" + }], "menus": { - "webview/context": [ - { - "command": "workbench.action.webview.openDeveloperTools", - "when": "frontMatter:isDevelopment" - } - ], - "editor/title": [ - { + "webview/context": [{ + "command": "workbench.action.webview.openDeveloperTools", + "when": "frontMatter:isDevelopment" + }], + "editor/title": [{ "command": "frontMatter.markup.heading", "group": "navigation@-133", "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" @@ -2449,14 +2436,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" @@ -2472,8 +2456,7 @@ "group": "frontmatter@3" } ], - "commandPalette": [ - { + "commandPalette": [{ "command": "frontMatter.init", "when": "frontMatterCanInit" }, @@ -2650,8 +2633,7 @@ "when": "frontMatter:file:isValid == true" } ], - "view/title": [ - { + "view/title": [{ "command": "frontMatter.chatbot", "group": "navigation@0", "when": "view == frontMatter.explorer" @@ -2683,16 +2665,13 @@ } ] }, - "languages": [ - { - "id": "frontmatter.project.output", - "mimetypes": [ - "text/x-code-output" - ] - } - ], - "grammars": [ - { + "languages": [{ + "id": "frontmatter.project.output", + "mimetypes": [ + "text/x-code-output" + ] + }], + "grammars": [{ "path": "./syntaxes/hugo.tmLanguage.json", "scopeName": "frontmatter.markdown.hugo", "injectTo": [ @@ -2705,48 +2684,45 @@ "path": "./syntaxes/frontmatter-output.tmLanguage.json" } ], - "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" - ] + "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:*", @@ -2874,4 +2850,4 @@ "dependencies": { "@radix-ui/react-dropdown-menu": "^2.0.6" } -} +} \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index 5a0c1174..115dc96c 100644 --- a/package.nls.json +++ b/package.nls.json @@ -73,6 +73,7 @@ "setting.frontMatter.content.pageFolders.items.properties.path.description": "Path of the folder", "setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description": "Exclude sub-directories", "setting.frontMatter.content.pageFolders.items.properties.previewPath.description": "Defines a custom preview path for the folder.", + "setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.", "setting.frontMatter.content.pageFolders.items.properties.filePrefix.description": "Defines a prefix for the file name.", "setting.frontMatter.content.pageFolders.items.properties.contentTypes.description": "Defines which content types can be used for the current location. If not defined, all content types will be available.", "setting.frontMatter.content.pageFolders.items.properties.disableCreation.description": "Disable the creation of new content in the folder.", @@ -176,6 +177,7 @@ "setting.frontMatter.panel.freeform.markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform)", "setting.frontMatter.panel.actions.disabled.markdownDescription": "Specify the actions you want to disable in the panel. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled)", "setting.frontMatter.preview.host.markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host)", + "setting.frontMatter.preview.trailingSlash.markdownDescription": "Specify if you want to add a trailing slash to the preview URL. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.trailingslash)", "setting.frontMatter.preview.pathName.markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.pathname)", "setting.frontMatter.site.baseURL.markdownDescription": "Specify the base URL of your site, this will be used for SEO checks. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.site.baseurl)", "setting.frontMatter.taxonomy.alignFilename.markdownDescription": "Align the filename with the new slug when it gets generated. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.alignfilename)", @@ -230,6 +232,7 @@ "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "Specify if the comparison is case sensitive. Default: true", "setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "Specify if you want to create a folder when creating new content.", "setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "Defines a custom preview path for the content type.", + "setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.", "setting.frontMatter.taxonomy.contentTypes.items.properties.slugTemplate.description": "Defines a custom slug template for the content type.", "setting.frontMatter.taxonomy.contentTypes.items.properties.template.description": "An optional template that can be used for creating new content.", "setting.frontMatter.taxonomy.contentTypes.items.properties.postScript.description": "An optional post script that can be used after new content creation.", diff --git a/src/commands/Article.ts b/src/commands/Article.ts index e39d2fef..dc536f57 100644 --- a/src/commands/Article.ts +++ b/src/commands/Article.ts @@ -321,7 +321,7 @@ export class Article { } const folderName = basename(dirname(file)); - return `${prefix}${folderName}${suffix}`; + return folderName; } /** diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index 79c65dd8..8a1a991b 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -9,7 +9,8 @@ import { PreviewCommands, SETTING_EXPERIMENTAL, SETTING_DATE_FORMAT, - GeneralCommands + GeneralCommands, + SETTING_PREVIEW_TRAILING_SLASH } from './../constants'; import { ArticleHelper } from './../helpers/ArticleHelper'; import { join, parse } from 'path'; @@ -312,11 +313,14 @@ export class Preview { pathname = processPathPlaceholders(pathname, relativePath, filePath, selectedFolder); const file = parse(filePath); - if ( - file.name.toLowerCase() === 'index' && - (pathname.endsWith(slug) || !pathname || pathname === '/') - ) { - slug = ''; + if (file.name.toLowerCase() === 'index') { + const cleanPathName = pathname.endsWith('/') + ? pathname.substring(0, pathname.length - 1) + : pathname; + + if (cleanPathName.endsWith(slug) || !pathname || pathname === '/') { + slug = ''; + } } } @@ -342,6 +346,24 @@ export class Preview { slug = slug.substring(0, slug.endsWith('_index') ? slug.length - 6 : slug.length - 5); } + // Add the trailing slash + let trailingSlash = false; + if (settings.trailingSlash !== undefined) { + trailingSlash = settings.trailingSlash; + } + + if (selectedFolder && selectedFolder.trailingSlash !== undefined) { + trailingSlash = selectedFolder.trailingSlash; + } + + if (contentType && contentType.trailingSlash !== undefined) { + trailingSlash = contentType.trailingSlash; + } + + if (trailingSlash && !slug.endsWith('/')) { + slug = `${slug}/`; + } + return slug; } @@ -378,10 +400,12 @@ export class Preview { public static getSettings(): PreviewSettings { const host = Settings.get(SETTING_PREVIEW_HOST); const pathname = Settings.get(SETTING_PREVIEW_PATHNAME); + const trailingSlash = Settings.get(SETTING_PREVIEW_TRAILING_SLASH); return { host, - pathname + pathname, + trailingSlash }; } diff --git a/src/constants/settings.ts b/src/constants/settings.ts index a6be1116..c1424792 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -52,6 +52,7 @@ export const SETTING_PANEL_ACTIONS_DISABLED = 'panel.actions.disabled'; export const SETTING_PREVIEW_HOST = 'preview.host'; export const SETTING_PREVIEW_PATHNAME = 'preview.pathName'; +export const SETTING_PREVIEW_TRAILING_SLASH = 'preview.trailingSlash'; export const SETTING_CUSTOM_SCRIPTS = 'custom.scripts'; diff --git a/src/models/ContentFolder.ts b/src/models/ContentFolder.ts index 8fb37b13..c8611a69 100644 --- a/src/models/ContentFolder.ts +++ b/src/models/ContentFolder.ts @@ -7,6 +7,7 @@ export interface ContentFolder { disableCreation?: boolean; excludeSubdir?: boolean; previewPath?: string; + trailingSlash?: boolean; filePrefix?: string; contentTypes?: string[]; originalPath?: string; diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index a65d79a8..b378fc7a 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -59,6 +59,7 @@ export interface ContentType { fileType?: 'md' | 'mdx' | string; previewPath?: string | null; + trailingSlash?: boolean; // Add this property for trailing slash option slugTemplate?: string; pageBundle?: boolean; defaultFileName?: string; @@ -229,6 +230,7 @@ export interface EnvironmentScript { export interface PreviewSettings { host: string | undefined; pathname: string | undefined; + trailingSlash: boolean | undefined; } export interface CustomTaxonomy {