From 4e040b5f7abde53556df00805eb5237d5a015d0f Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Fri, 15 Oct 2021 14:02:08 +0200 Subject: [PATCH 01/16] 5.2.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6be4108d..507bbc3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-front-matter-beta", - "version": "5.1.1", + "version": "5.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0bceb0bb..f37e4b85 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Front Matter", "description": "An essential Visual Studio Code extension when you want to manage the markdown pages of your static site like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...", "icon": "assets/frontmatter-teal-128x128.png", - "version": "5.1.1", + "version": "5.2.0", "preview": false, "publisher": "eliostruyf", "galleryBanner": { From d19e632f804555a29d00572f4964dfd8ca642fa7 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Fri, 15 Oct 2021 16:11:59 +0200 Subject: [PATCH 02/16] #151 #152 - Framework detection + preset public folder --- package.json | 5 ++ src/commands/Dashboard.ts | 32 ++++++++- src/constants/FrameworkDetectors.ts | 21 ++++++ src/constants/settings.ts | 2 + src/dashboardWebView/DashboardMessage.ts | 3 +- .../components/Steps/Step.tsx | 41 +++++++---- .../components/Steps/StepsToGetStarted.tsx | 71 +++++++++++++++++-- .../components/WelcomeScreen.tsx | 4 +- src/dashboardWebView/models/Settings.ts | 4 +- src/helpers/FrameworkDetector.ts | 43 +++++++++++ src/models/Framework.ts | 8 +++ src/models/index.ts | 4 ++ 12 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 src/constants/FrameworkDetectors.ts create mode 100644 src/helpers/FrameworkDetector.ts create mode 100644 src/models/Framework.ts diff --git a/package.json b/package.json index f37e4b85..a74b7c0b 100644 --- a/package.json +++ b/package.json @@ -180,6 +180,11 @@ "markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.openonstart)", "scope": "Dashboard" }, + "frontMatter.framework.id": { + "type": "string", + "default": "", + "markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.framework.id)" + }, "frontMatter.panel.freeform": { "type": "boolean", "default": true, diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index c0487931..232eb560 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -1,10 +1,10 @@ -import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES, DefaultFields, HOME_PAGE_NAVIGATION_ID, ExtensionState, COMMAND_NAME } from '../constants'; +import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES, DefaultFields, HOME_PAGE_NAVIGATION_ID, ExtensionState, COMMAND_NAME, SETTINGS_FRAMEWORK_ID } from '../constants'; import { ArticleHelper } from './../helpers/ArticleHelper'; import { basename, dirname, extname, join, parse } from "path"; import { existsSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs"; import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env, Position } from "vscode"; import { Settings as SettingsHelper } from '../helpers'; -import { TaxonomyType } from '../models'; +import { Framework, TaxonomyType } from '../models'; import { Folders } from './Folders'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { DashboardMessage } from '../dashboardWebView/DashboardMessage'; @@ -24,6 +24,7 @@ import { MediaLibrary } from '../helpers/MediaLibrary'; import imageSize from 'image-size'; import { parseWinPath } from '../helpers/parseWinPath'; import { DateHelper } from '../helpers/DateHelper'; +import { FrameworkDetector } from '../helpers/FrameworkDetector'; export class Dashboard { private static webview: WebviewPanel | null = null; @@ -187,6 +188,9 @@ export class Dashboard { case DashboardMessage.createMediaFolder: await commands.executeCommand(COMMAND_NAME.createFolder, msg?.data); break; + case DashboardMessage.setFramework: + Dashboard.setFramework(msg?.data); + break; } }); } @@ -257,6 +261,8 @@ export class Dashboard { private static async getSettings() { const ext = Extension.getInstance(); const wsFolder = Folders.getWorkspaceFolder(); + // const isInitialized = await Template.isInitialized(); + const isInitialized = false; Dashboard.postWebviewMessage({ command: DashboardCommand.settings, @@ -265,7 +271,7 @@ export class Dashboard { wsFolder: wsFolder ? wsFolder.fsPath : '', staticFolder: SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER), folders: Folders.get(), - initialized: await Template.isInitialized(), + initialized: isInitialized, tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag), categories: SettingsHelper.getTaxonomy(TaxonomyType.Category), openOnStart: SettingsHelper.get(SETTINGS_DASHBOARD_OPENONSTART), @@ -274,10 +280,30 @@ export class Dashboard { mediaSnippet: SettingsHelper.get(SETTINGS_DASHBOARD_MEDIA_SNIPPET) || [], contentTypes: SettingsHelper.get(SETTING_TAXONOMY_CONTENT_TYPES) || [], contentFolders: Folders.get().map(f => f.path), + crntFramework: SettingsHelper.get(SETTINGS_FRAMEWORK_ID), + framework: (!isInitialized && wsFolder) ? FrameworkDetector.get(wsFolder.fsPath) : null, } as Settings }); } + /** + * Set the current site-generator or framework + related settings + * @param frameworkId + */ + private static setFramework(frameworkId: string | null) { + SettingsHelper.update(SETTINGS_FRAMEWORK_ID, frameworkId, true); + + if (frameworkId) { + const allFrameworks = FrameworkDetector.getAll(); + const framework = allFrameworks.find((f: Framework) => f.name === frameworkId); + if (framework) { + SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, framework.static, true); + } else { + SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, "", true); + } + } + } + /** * Update a setting from the dashboard */ diff --git a/src/constants/FrameworkDetectors.ts b/src/constants/FrameworkDetectors.ts new file mode 100644 index 00000000..5433233d --- /dev/null +++ b/src/constants/FrameworkDetectors.ts @@ -0,0 +1,21 @@ +export const FrameworkDetectors = [ + { + "framework": {"name": "gatsby", "dist": "public", "static": "static", "build": "gatsby build"}, + "requiredFiles": ["gatsby-config.js"], + "requiredDependencies": ["gatsby"] + }, + { + "framework": {"name": "hugo", "dist": "public", "static": "static", "build": "hugo"}, + "requiredFiles": ["config.toml", "config.yaml", "config.yml"] + }, + { + "framework": {"name": "next", "dist": ".next", "static": "public", "build": "next build"}, + "requiredFiles": ["next.config.js"], + "requiredDependencies": ["next"] + }, + { + "framework": {"name": "nuxt", "dist": "dist", "static": "static", "build": "nuxt"}, + "requiredFiles": ["nuxt.config.js"], + "requiredDependencies": ["nuxt"] + } +]; \ No newline at end of file diff --git a/src/constants/settings.ts b/src/constants/settings.ts index fa0f92b2..4643dbc0 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -42,6 +42,8 @@ export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight"; export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart"; export const SETTINGS_DASHBOARD_MEDIA_SNIPPET = "dashboard.mediaSnippet"; +export const SETTINGS_FRAMEWORK_ID = "framework.id"; + /** * @deprecated */ diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index cb86a9c6..242bec0c 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -17,5 +17,6 @@ export enum DashboardMessage { deleteMedia = 'deleteMedia', insertPreviewImage = 'insertPreviewImage', updateMediaMetadata = 'updateMediaMetadata', - createMediaFolder = 'createMediaFolder' + createMediaFolder = 'createMediaFolder', + setFramework = 'setFramework', } \ No newline at end of file diff --git a/src/dashboardWebView/components/Steps/Step.tsx b/src/dashboardWebView/components/Steps/Step.tsx index 7e961ef4..553f40f3 100644 --- a/src/dashboardWebView/components/Steps/Step.tsx +++ b/src/dashboardWebView/components/Steps/Step.tsx @@ -4,22 +4,17 @@ import { Status } from '../../models/Status'; export interface IStepProps { name: string; - description: string; + description: JSX.Element; status: Status; showLine: boolean; - onClick?: () => void; + onClick?: () => void | undefined; } export const Step: React.FunctionComponent = ({name, description, status, showLine, onClick}: React.PropsWithChildren) => { - return ( - <> - { - showLine ? ( - ); -}; \ No newline at end of file +}; + +SeoDetails.displayName = 'SeoDetails'; +export { SeoDetails }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoFieldInfo.tsx b/src/panelWebView/components/SeoFieldInfo.tsx index 2b13dd05..d61cf4f5 100644 --- a/src/panelWebView/components/SeoFieldInfo.tsx +++ b/src/panelWebView/components/SeoFieldInfo.tsx @@ -9,7 +9,7 @@ export interface ISeoFieldInfoProps { isValid?: boolean; } -export const SeoFieldInfo: React.FunctionComponent = ({ title, value, recommendation, isValid }: React.PropsWithChildren) => { +const SeoFieldInfo: React.FunctionComponent = ({ title, value, recommendation, isValid }: React.PropsWithChildren) => { return ( {title} @@ -19,4 +19,7 @@ export const SeoFieldInfo: React.FunctionComponent = ({ titl ); -}; \ No newline at end of file +}; + +SeoFieldInfo.displayName = 'SeoFieldInfo'; +export { SeoFieldInfo }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoKeywordInfo.tsx b/src/panelWebView/components/SeoKeywordInfo.tsx index 4f2dd8d8..bdf0b451 100644 --- a/src/panelWebView/components/SeoKeywordInfo.tsx +++ b/src/panelWebView/components/SeoKeywordInfo.tsx @@ -10,7 +10,7 @@ export interface ISeoKeywordInfoProps { content: string; } -export const SeoKeywordInfo: React.FunctionComponent = ({keyword, title, description, slug, content}: React.PropsWithChildren) => { +const SeoKeywordInfo: React.FunctionComponent = ({keyword, title, description, slug, content}: React.PropsWithChildren) => { if (!keyword) { return null; @@ -33,4 +33,7 @@ export const SeoKeywordInfo: React.FunctionComponent = ({k ); -}; \ No newline at end of file +}; + +SeoKeywordInfo.displayName = 'SeoKeywordInfo'; +export { SeoKeywordInfo }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoKeywords.tsx b/src/panelWebView/components/SeoKeywords.tsx index 46af35d9..177804d5 100644 --- a/src/panelWebView/components/SeoKeywords.tsx +++ b/src/panelWebView/components/SeoKeywords.tsx @@ -11,7 +11,7 @@ export interface ISeoKeywordsProps { content: string; } -export const SeoKeywords: React.FunctionComponent = ({keywords, ...data}: React.PropsWithChildren) => { +const SeoKeywords: React.FunctionComponent = ({keywords, ...data}: React.PropsWithChildren) => { const validateKeywords = () => { if (!keywords) { @@ -55,8 +55,9 @@ export const SeoKeywords: React.FunctionComponent = ({keyword } - - ); -}; \ No newline at end of file +}; + +SeoKeywords.displayName = 'SeoKeywords'; +export { SeoKeywords }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoStatus.tsx b/src/panelWebView/components/SeoStatus.tsx index 407b562c..357a8568 100644 --- a/src/panelWebView/components/SeoStatus.tsx +++ b/src/panelWebView/components/SeoStatus.tsx @@ -11,14 +11,34 @@ export interface ISeoStatusProps { data: any; } -export const SeoStatus: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SeoStatus: React.FunctionComponent = (props: React.PropsWithChildren) => { const { data, seo } = props; const { title } = data; const [ isOpen, setIsOpen ] = React.useState(true); const tableRef = React.useRef(); + const pushUpdate = React.useRef((value: boolean) => { + setTimeout(() => { + setIsOpen(value); + }, 10); + }).current; const { descriptionField } = seo; + // Workaround for lit components not updating render + React.useEffect(() => { + setTimeout(() => { + let height = 0; + + tableRef.current?.childNodes.forEach((elm: any) => { + height += elm.clientHeight; + }); + + if (height > 0 && tableRef.current) { + tableRef.current.style.height = `${height}px`; + } + }, 10); + }, [title, data[descriptionField], data?.articleDetails?.wordCount]); + if (!title && !data[descriptionField]) { return null; } @@ -72,24 +92,14 @@ export const SeoStatus: React.FunctionComponent = (props: React ); }; - // Workaround for lit components not updating render - React.useEffect(() => { - setTimeout(() => { - let height = 0; - - tableRef.current?.childNodes.forEach((elm: any) => { - height += elm.clientHeight; - }); - - if (height > 0 && tableRef.current) { - tableRef.current.style.height = `${height}px`; - } - }, 10); - }, [title, data[descriptionField], data?.articleDetails?.wordCount]); + return ( - setIsOpen(value)}> + { renderContent() } ); -}; \ No newline at end of file +}; + +SeoStatus.displayName = 'SeoStatus'; +export { SeoStatus }; \ No newline at end of file diff --git a/src/panelWebView/components/SlugAction.tsx b/src/panelWebView/components/SlugAction.tsx index 85f57809..15967aad 100644 --- a/src/panelWebView/components/SlugAction.tsx +++ b/src/panelWebView/components/SlugAction.tsx @@ -11,7 +11,7 @@ export interface ISlugActionProps { slugOpts: Slug; } -export const SlugAction: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SlugAction: React.FunctionComponent = (props: React.PropsWithChildren) => { const { value, crntValue, slugOpts } = props; let slug = SlugHelper.createSlug(value); @@ -24,4 +24,7 @@ export const SlugAction: React.FunctionComponent = (props: Rea return ( ); -}; \ No newline at end of file +}; + +SlugAction.displayName = 'SlugAction'; +export { SlugAction }; \ No newline at end of file diff --git a/src/panelWebView/components/Spinner.tsx b/src/panelWebView/components/Spinner.tsx index 4b0d34ac..ebb4d187 100644 --- a/src/panelWebView/components/Spinner.tsx +++ b/src/panelWebView/components/Spinner.tsx @@ -2,8 +2,11 @@ import * as React from 'react'; export interface ISpinnerProps {} -export const Spinner: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Spinner: React.FunctionComponent = (props: React.PropsWithChildren) => { return (
Loading...
); -}; \ No newline at end of file +}; + +Spinner.displayName = 'Spinner'; +export { Spinner }; \ No newline at end of file diff --git a/src/panelWebView/components/SponsorMsg.tsx b/src/panelWebView/components/SponsorMsg.tsx index c0c92b48..e1c6c6bd 100644 --- a/src/panelWebView/components/SponsorMsg.tsx +++ b/src/panelWebView/components/SponsorMsg.tsx @@ -4,7 +4,7 @@ import { HeartIcon } from './Icons/HeartIcon'; export interface ISponsorMsgProps {} -export const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => { return (

@@ -12,4 +12,7 @@ export const SponsorMsg: React.FunctionComponent = (props: Rea

); -}; \ No newline at end of file +}; + +SponsorMsg.displayName = 'SponsorMsg'; +export { SponsorMsg }; \ No newline at end of file diff --git a/src/panelWebView/components/Tag.tsx b/src/panelWebView/components/Tag.tsx index 03f900e5..23527b0f 100644 --- a/src/panelWebView/components/Tag.tsx +++ b/src/panelWebView/components/Tag.tsx @@ -13,7 +13,7 @@ export interface ITagProps { onRemove: (tags: string) => void; } -export const Tag: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Tag: React.FunctionComponent = (props: React.PropsWithChildren) => { const { value, className, title, onRemove, onCreate, disableConfigurable } = props; return ( @@ -25,4 +25,7 @@ export const Tag: React.FunctionComponent = (props: React.PropsWithCh ); -}; \ No newline at end of file +}; + +Tag.displayName = 'Tag'; +export { Tag }; \ No newline at end of file diff --git a/src/panelWebView/components/TagPicker.tsx b/src/panelWebView/components/TagPicker.tsx index 9be777ef..e25fc294 100644 --- a/src/panelWebView/components/TagPicker.tsx +++ b/src/panelWebView/components/TagPicker.tsx @@ -20,7 +20,7 @@ export interface ITagPickerProps { disableConfigurable?: boolean; } -export const TagPicker: React.FunctionComponent = (props: React.PropsWithChildren) => { +const TagPicker: React.FunctionComponent = (props: React.PropsWithChildren) => { const { label, icon, type, crntSelected, options, freeform, focussed, unsetFocus, disableConfigurable } = props; const [ selected, setSelected ] = React.useState([]); const [ inputValue, setInputValue ] = React.useState(""); @@ -194,4 +194,7 @@ export const TagPicker: React.FunctionComponent = (props: React disableConfigurable={!!disableConfigurable} /> ); -}; \ No newline at end of file +}; + +TagPicker.displayName = 'TagPicker'; +export { TagPicker }; \ No newline at end of file diff --git a/src/panelWebView/components/Tags.tsx b/src/panelWebView/components/Tags.tsx index 9a5091a3..a7f2cead 100644 --- a/src/panelWebView/components/Tags.tsx +++ b/src/panelWebView/components/Tags.tsx @@ -11,7 +11,7 @@ export interface ITagsProps { onRemove: (tags: string) => void; } -export const Tags: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Tags: React.FunctionComponent = (props: React.PropsWithChildren) => { const { values, options, onCreate, onRemove, disableConfigurable } = props; const knownTags = values.filter(v => options.includes(v)); @@ -38,4 +38,7 @@ export const Tags: React.FunctionComponent = (props: React.PropsWith } ); -}; \ No newline at end of file +}; + +Tags.displayName = 'Tags'; +export { Tags }; \ No newline at end of file diff --git a/src/panelWebView/components/ValidInfo.tsx b/src/panelWebView/components/ValidInfo.tsx index 9936018a..431f923a 100644 --- a/src/panelWebView/components/ValidInfo.tsx +++ b/src/panelWebView/components/ValidInfo.tsx @@ -6,7 +6,7 @@ export interface IValidInfoProps { isValid: boolean; } -export const ValidInfo: React.FunctionComponent = ({isValid}: React.PropsWithChildren) => { +const ValidInfo: React.FunctionComponent = ({isValid}: React.PropsWithChildren) => { return ( <> { @@ -18,4 +18,7 @@ export const ValidInfo: React.FunctionComponent = ({isValid}: R } ); -}; \ No newline at end of file +}; + +ValidInfo.displayName = 'ValidInfo'; +export { ValidInfo }; \ No newline at end of file From 33093e1eb48c62cbb47be89e40072124092793cb Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Tue, 19 Oct 2021 14:56:49 +0200 Subject: [PATCH 15/16] Change useEffect order --- src/panelWebView/components/Fields/DateTimeField.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/panelWebView/components/Fields/DateTimeField.tsx b/src/panelWebView/components/Fields/DateTimeField.tsx index 8b125ceb..7d63cf2a 100644 --- a/src/panelWebView/components/Fields/DateTimeField.tsx +++ b/src/panelWebView/components/Fields/DateTimeField.tsx @@ -24,11 +24,6 @@ const CustomInput = forwardRef(({ value, onClick } export const DateTimeField: React.FunctionComponent = ({label, date, format, onChange}: React.PropsWithChildren) => { const [ dateValue, setDateValue ] = React.useState(null); - - const onDateChange = (date: Date) => { - setDateValue(date); - onChange(date); - }; React.useEffect(() => { const crntValue = DateHelper.tryParse(date, format); @@ -38,6 +33,11 @@ export const DateTimeField: React.FunctionComponent = ({lab setDateValue(date); } }, [ date ]); + + const onDateChange = (date: Date) => { + setDateValue(date); + onChange(date); + }; return (
From 8902e25021e487740aa839df620189bb84ed350c Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Tue, 19 Oct 2021 15:40:17 +0200 Subject: [PATCH 16/16] Release 5.2.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a936dc65..7d00319d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [5.2.0] - 2021-XX-XX +## [5.2.0] - 2021-10-19 ### 🎨 Enhancements