#226 - Allow to start the local server for the framework or SSG

This commit is contained in:
Elio Struyf
2022-01-19 10:15:58 +01:00
parent e68daa8ac2
commit 42cc53cefc
11 changed files with 158 additions and 29 deletions
+5
View File
@@ -522,6 +522,11 @@
"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.framework.startCommand": {
"type": ["string", "null"],
"default": null,
"markdownDescription": "Specify the command you want to use to start your static site generator or framework. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.framework.startcommand)"
},
"frontMatter.media.defaultSorting": {
"type": "string",
"default": "",
+16 -4
View File
@@ -2,20 +2,32 @@ export const FrameworkDetectors = [
{
"framework": {"name": "gatsby", "dist": "public", "static": "static", "build": "gatsby build"},
"requiredFiles": ["gatsby-config.js"],
"requiredDependencies": ["gatsby"]
"requiredDependencies": ["gatsby"],
"commands": {
"start": "npx gatsby develop"
}
},
{
"framework": {"name": "hugo", "dist": "public", "static": "static", "build": "hugo"},
"requiredFiles": ["config.toml", "config.yaml", "config.yml"]
"requiredFiles": ["config.toml", "config.yaml", "config.yml"],
"commands": {
"start": "hugo server -D"
}
},
{
"framework": {"name": "next", "dist": ".next", "static": "public", "build": "next build"},
"requiredFiles": ["next.config.js"],
"requiredDependencies": ["next"]
"requiredDependencies": ["next"],
"commands": {
"start": "npx next dev"
}
},
{
"framework": {"name": "nuxt", "dist": "dist", "static": "static", "build": "nuxt"},
"requiredFiles": ["nuxt.config.js"],
"requiredDependencies": ["nuxt"]
"requiredDependencies": ["nuxt"],
"commands": {
"start": "npx nuxt"
}
}
];
+1
View File
@@ -62,6 +62,7 @@ export const SETTINGS_DATA_FOLDERS = "data.folders";
export const SETTINGS_DATA_TYPES = "data.types";
export const SETTINGS_FRAMEWORK_ID = "framework.id";
export const SETTINGS_FRAMEWORK_START = "framework.startCommand";
export const SETTING_SITE_BASEURL = "site.baseURL";
+44 -25
View File
@@ -1,9 +1,9 @@
import { DashboardData } from '../models/DashboardData';
import { Template } from '../commands/Template';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS, SETTINGS_CONTENT_DRAFT_FIELD, SETTING_SEO_SLUG_LENGTH, SETTING_SITE_BASEURL, SETTING_TAXONOMY_CUSTOM, CONTEXT } from '../constants';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS, SETTINGS_CONTENT_DRAFT_FIELD, SETTING_SEO_SLUG_LENGTH, SETTING_SITE_BASEURL, SETTING_TAXONOMY_CUSTOM, CONTEXT, SETTINGS_FRAMEWORK_ID, SETTINGS_FRAMEWORK_START } from '../constants';
import * as os from 'os';
import { PanelSettings, CustomScript as ICustomScript } from '../models/PanelSettings';
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv, ThemeIcon } from "vscode";
import { ArticleHelper, Settings } from "../helpers";
import { Command } from "../panelWebView/Command";
import { CommandToCode } from '../panelWebView/CommandToCode';
@@ -161,13 +161,13 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
await commands.executeCommand(COMMAND_NAME.createTemplate);
break;
case CommandToCode.updateModifiedUpdating:
this.updateModifiedUpdating(msg.data || false);
this.updateSetting(SETTING_AUTO_UPDATE_DATE, msg.data || false);
break;
case CommandToCode.toggleWritingSettings:
this.toggleWritingSettings();
break;
case CommandToCode.updateFmHighlight:
this.updateFmHighlight((msg.data !== null && msg.data !== undefined) ? msg.data : false);
this.updateSetting(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, (msg.data !== null && msg.data !== undefined) ? msg.data : false);
break;
case CommandToCode.toggleCenterMode:
await commands.executeCommand(`workbench.action.toggleCenteredLayout`);
@@ -179,7 +179,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
await commands.executeCommand(COMMAND_NAME.dashboard);
break;
case CommandToCode.updatePreviewUrl:
this.updatePreviewUrl(msg.data || "");
this.updateSetting(SETTING_PREVIEW_HOST, msg.data || "");
break;
case CommandToCode.openInEditor:
openFileInEditor(msg.data);
@@ -194,6 +194,12 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
} as DashboardData);
this.getMediaSelection();
break;
case CommandToCode.frameworkCommand:
this.openTerminalWithCommand(msg.data.command);
break;
case CommandToCode.updateStartCommand:
await this.updateSetting(SETTINGS_FRAMEWORK_START, msg.data || "");
break;
}
});
@@ -344,6 +350,29 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
this.pushMetadata(article.data);
}
/**
* Open a terminal and run the passed command
* @param command
*/
private openTerminalWithCommand(command: string) {
if (command) {
let terminal = window.activeTerminal;
if (!terminal || (terminal && terminal.state.isInteractedWith === true)) {
terminal = window.createTerminal({
name: `Starting local server: ${command}`,
iconPath: new ThemeIcon('server-environment'),
message: `Starting local server: ${command}`,
});
}
if (terminal) {
terminal.sendText(command);
terminal.show(false);
}
}
}
/**
* Run a custom script
* @param msg
@@ -405,7 +434,11 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
contentTypes: Settings.get(SETTING_TAXONOMY_CONTENT_TYPES) || [],
dashboardViewData: Dashboard.viewData,
draftField: Settings.get<DraftField>(SETTINGS_CONTENT_DRAFT_FIELD),
isBacker: await Extension.getInstance().getState<boolean | undefined>(CONTEXT.backer, 'global')
isBacker: await Extension.getInstance().getState<boolean | undefined>(CONTEXT.backer, 'global'),
framework: Settings.get<string>(SETTINGS_FRAMEWORK_ID),
commands: {
start: Settings.get<string>(SETTINGS_FRAMEWORK_START)
}
} as PanelSettings
});
}
@@ -702,26 +735,12 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
}
/**
* Update the preview URL
* Updates a setting and refreshes the retrieved settings
* @param setting
* @param value
*/
private async updatePreviewUrl(previewUrl: string) {
await Settings.update(SETTING_PREVIEW_HOST, previewUrl);
this.getSettings();
}
/**
* Toggle the Front Matter highlighting
*/
private async updateFmHighlight(autoUpdate: boolean) {
await Settings.update(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, autoUpdate);
this.getSettings();
}
/**
* Toggle the modified auto-update setting
*/
private async updateModifiedUpdating(autoUpdate: boolean) {
await Settings.update(SETTING_AUTO_UPDATE_DATE, autoUpdate);
private async updateSetting(setting: string, value: any) {
await Settings.update(setting, value);
this.getSettings();
}
+6
View File
@@ -21,6 +21,12 @@ export interface PanelSettings {
dashboardViewData: DashboardData | undefined;
draftField: DraftField;
isBacker: boolean | undefined;
framework: string | undefined;
commands: FrameworkCommands;
}
export interface FrameworkCommands {
start: string | undefined;
}
export interface ContentType {
+2
View File
@@ -28,4 +28,6 @@ export enum CommandToCode {
selectImage = "select-image",
updateCustomTaxonomy = "updateCustomTaxonomy",
addToCustomTaxonomy = "addToCustomTaxonomy",
frameworkCommand = "framework-command",
updateStartCommand = "update-start-command",
}
+3
View File
@@ -4,6 +4,7 @@ import { Collapsible } from './Collapsible';
import { CustomScript } from './CustomScript';
import { Preview } from './Preview';
import { SlugAction } from './SlugAction';
import { StartServerButton } from './StartServerButton';
export interface IActionsProps {
metadata: any;
@@ -24,6 +25,8 @@ const Actions: React.FunctionComponent<IActionsProps> = ({ metadata, settings }:
{ settings?.preview?.host && <Preview slug={metadata.slug} /> }
<StartServerButton settings={settings} />
{
(settings && settings.scripts && settings.scripts.length > 0) && (
settings.scripts.map((value, idx) => (
+2
View File
@@ -7,6 +7,7 @@ import { GlobalSettings } from './GlobalSettings';
import { OtherActions } from './OtherActions';
import { FolderAndFiles } from './FolderAndFiles';
import { SponsorMsg } from './SponsorMsg';
import { StartServerButton } from './StartServerButton';
export interface IBaseViewProps {
settings: PanelSettings | undefined;
@@ -45,6 +46,7 @@ const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings, folderAndF
<Collapsible id={`base_actions`} title="Actions">
<div className={`base__actions`}>
<button onClick={openDashboard}>Open dashboard</button>
<StartServerButton settings={settings} />
<button onClick={initProject} disabled={settings?.isInitialized}>Initialize project</button>
<button onClick={createContent} disabled={!settings?.isInitialized}>Create new content</button>
<button onClick={openPreview} disabled={!settings?.preview?.host}>Open site preview</button>
@@ -5,6 +5,7 @@ import { MessageHelper } from '../../helpers/MessageHelper';
import { useDebounce } from '../../hooks/useDebounce';
import { Collapsible } from './Collapsible';
import { VsCheckbox, VsLabel } from './VscodeComponents';
import useStartCommand from '../hooks/useStartCommand';
export interface IGlobalSettingsProps {
settings: PanelSettings | undefined;
@@ -14,7 +15,11 @@ export interface IGlobalSettingsProps {
const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings, isBase}: React.PropsWithChildren<IGlobalSettingsProps>) => {
const { modifiedDateUpdate, fmHighlighting } = settings || {};
const [ previewUrl, setPreviewUrl ] = React.useState<string>("");
const [ startCommandValue, setStartCommandValue ] = React.useState<string | null>(null);
const [ isDirty, setIsDirty ] = React.useState<boolean>(false);
const { startCommand } = useStartCommand(settings);
const debounceStartCommand = useDebounce(startCommandValue, 1000);
const debouncePreviewUrl = useDebounce(previewUrl, 1000);
const onDateCheck = () => {
@@ -30,12 +35,21 @@ const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings
setPreviewUrl(e.currentTarget.value);
};
const updateStartCommand = (e: React.ChangeEvent<HTMLInputElement>) => {
setIsDirty(true);
setStartCommandValue(e.currentTarget.value);
};
React.useEffect(() => {
if (settings?.preview.host) {
setPreviewUrl(settings.preview.host);
}
}, [settings?.preview.host]);
React.useEffect(() => {
setStartCommandValue(startCommand);
}, [startCommand]);
React.useEffect(() => {
if (isDirty) {
setIsDirty(false);
@@ -43,6 +57,13 @@ const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings
}
}, [debouncePreviewUrl]);
React.useEffect(() => {
if (isDirty) {
setIsDirty(false);
MessageHelper.sendMessage(CommandToCode.updateStartCommand, debounceStartCommand);
}
}, [debounceStartCommand]);
return (
<>
<Collapsible id={`${isBase ? "base_" : ""}settings`} className={`base__actions`} title="Global settings">
@@ -62,6 +83,14 @@ const GlobalSettings: React.FunctionComponent<IGlobalSettingsProps> = ({settings
value={previewUrl}
onChange={previewChange} />
</div>
<div className={`base__action`}>
<VsLabel>Local server command</VsLabel>
<input
type={`text`}
placeholder="Example: hugo server -D"
value={startCommandValue || ""}
onChange={updateStartCommand} />
</div>
</Collapsible>
</>
);
@@ -0,0 +1,22 @@
import * as React from 'react';
import { FrameworkDetectors } from '../../constants/FrameworkDetectors';
import { MessageHelper } from '../../helpers/MessageHelper';
import { PanelSettings } from '../../models';
import { CommandToCode } from '../CommandToCode';
import useStartCommand from '../hooks/useStartCommand';
export interface IStartServerButtonProps {
settings: PanelSettings | undefined;
}
export const StartServerButton: React.FunctionComponent<IStartServerButtonProps> = ({settings}: React.PropsWithChildren<IStartServerButtonProps>) => {
const { startCommand } = useStartCommand(settings);
const startLocalServer = (command: string) => {
MessageHelper.sendMessage(CommandToCode.frameworkCommand, { command });
};
return (
startCommand ? <button onClick={() => startLocalServer(startCommand)}>Start server</button> : null
);
};
@@ -0,0 +1,28 @@
import { useState, useEffect } from 'react';
import { FrameworkDetectors } from '../../constants/FrameworkDetectors';
import { PanelSettings } from '../../models';
export default function useStartCommand(settings?: PanelSettings) {
const [startCommand, setStartCommand] = useState<string | null>(null);
useEffect(() => {
if (settings?.commands?.start) {
setStartCommand(settings?.commands?.start);
return;
}
let command: string = '';
if (settings?.framework) {
const framework = FrameworkDetectors.find(f => f.framework.name === settings.framework);
if (framework?.commands?.start) {
command = framework.commands.start;
}
}
setStartCommand(command);
}, [settings?.framework, settings?.commands?.start]);
return {
startCommand
};
}