Implementation of custom scripts

This commit is contained in:
Elio Struyf
2021-05-04 11:28:26 +02:00
parent 6949288454
commit e0ca17ec65
14 changed files with 153 additions and 5 deletions
+4
View File
@@ -0,0 +1,4 @@
# These are supported funding model platforms
github: [estruyf]
custom: ["https://www.buymeacoffee.com/zMeFRy9"]
+28
View File
@@ -0,0 +1,28 @@
name: Release
on:
release:
types:
- published
workflow_dispatch:
jobs:
build:
name: "Build and release"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 14
registry-url: https://registry.npmjs.org/
- name: Install the dependencies
run: npm i
- name: Install vsce
run: npm i -g vsce
- name: Publish
run: vsce publish -p ${{ secrets.VSCE_PAT }}
+4
View File
@@ -1,5 +1,9 @@
# Change Log
## [1.15.0] - 2020-05-04
- Added the ability to add your own custom scripts as panel actions.
## [1.14.0] - 2020-03-19
- New links added to the Front Matter panel to reveal the file and folder.
+31
View File
@@ -22,6 +22,24 @@ To leverage most of the capabilities of the extension. SEO information and every
> **Info**: By default, the tags/categories picker allows you to insert existing and none tags/categories. When you enter a none existing tag/category, the panel shows an add `+` icon in front of that button. This functionality allows you to store this tag/category in your settings. If you want to disable this feature, you can do that by setting the `frontMatter.panel.freeform` setting to `false`.
Since version `1.15.0`, the extension allows you to create your own custom actions, by running Node.js scripts from your project. In order to use this functionality, you will need to configure the [`frontMatter.custom.scripts`](#frontMatter.custom.scripts) setting for your project.
Once a custom action has been configured, it will appear on the Front Matter panel.
![](./assets/custom-actions.png)
The current workspace- and file-path will be passed as an argument. In your script fetch these arguments as follows:
```javascript
const arguments = process.argv;
const workspaceArg = arguments[2];
const fileArg = arguments[3];
```
The output of the script will be passed as a notification, and it allows you to copy the output.
![](./assets/custom-action-notification.png)
## Creating articles from templates
By default, the extension looks for files stored in a `.templates` folder that should be located in your website project's root.
@@ -170,6 +188,19 @@ Specifies the modified date field name to use in your Front Matter. Default valu
}
```
### `frontMatter.custom.scripts`
Allows you to specify a title and script path (starting relative from the root of your project). These values will be used to create custom actions on the Front Matter panel. Default value: `[]`.
```json
{
"frontMatter.custom.scripts": [{
"title": "Generate social image",
"script": "./scripts/social-img.js"
}]
}
```
## Usage
- Start by opening the command prompt:
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

+5
View File
@@ -151,6 +151,11 @@
"type": "boolean",
"default": true,
"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."
},
"frontMatter.custom.scripts": {
"type": "array",
"default": [],
"markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument."
}
}
},
+3 -1
View File
@@ -22,4 +22,6 @@ export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
export const SETTING_TEMPLATES_FOLDER = "templates.folder";
export const SETTING_TEMPLATES_PREFIX = "templates.prefix";
export const SETTING_PANEL_FREEFORM = "panel.freeform";
export const SETTING_PANEL_FREEFORM = "panel.freeform";
export const SETTING_CUSTOM_SCRIPTS = "custom.scripts";
+6
View File
@@ -5,6 +5,7 @@ export interface PanelSettings {
tags: string[];
categories: string[];
freeform: boolean;
scripts: CustomScript[];
}
export interface SEO {
@@ -15,4 +16,9 @@ export interface SEO {
export interface Slug {
prefix: number;
suffix: number;
}
export interface CustomScript {
title: string;
script: string;
}
+2 -1
View File
@@ -10,5 +10,6 @@ export enum CommandToCode {
addCategoryToSettings = "add-category",
openSettings = "open-settings",
openFile = "open-file",
openProject = "open-project"
openProject = "open-project",
runCustomScript = "custom-script"
}
+1
View File
@@ -48,6 +48,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
{
settings && metadata && <Actions metadata={metadata} settings={settings} />
}
{
(settings && settings.tags && settings.tags.length > 0) && (
<TagPicker type={TagType.tags}
+9
View File
@@ -1,5 +1,6 @@
import * as React from 'react';
import { PanelSettings } from '../../models/PanelSettings';
import { CustomScript } from './CustomScript';
import { DateAction } from './DateAction';
import { PublishAction } from './PublishAction';
import { SlugAction } from './SlugAction';
@@ -25,6 +26,14 @@ export const Actions: React.FunctionComponent<IActionsProps> = (props: React.Pro
<DateAction />
{ metadata && typeof metadata.draft !== undefined && <PublishAction draft={metadata.draft} />}
{
(settings && settings.scripts && settings.scripts.length > 0) && (
settings.scripts.map((value) => (
<CustomScript {...value} />
))
)
}
</div>
);
};
+21
View File
@@ -0,0 +1,21 @@
import * as React from 'react';
import { CommandToCode } from '../CommandToCode';
import { MessageHelper } from '../helper/MessageHelper';
export interface ICustomScriptProps {
title: string;
script: string;
}
export const CustomScript: React.FunctionComponent<ICustomScriptProps> = ({title, script}: React.PropsWithChildren<ICustomScriptProps>) => {
const runCustomScript = () => {
MessageHelper.sendMessage(CommandToCode.runCustomScript, { title, script });
};
return (
<div className={`article__action`}>
<button onClick={runCustomScript}>{title}</button>
</div>
);
};
+39 -3
View File
@@ -1,6 +1,7 @@
import { SETTING_CUSTOM_SCRIPTS } from './../constants/settings';
import * as os from 'os';
import { PanelSettings } from './../models/PanelSettings';
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands } from "vscode";
import { PanelSettings, CustomScript } from './../models/PanelSettings';
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
import { CONFIG_KEY, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants";
import { ArticleHelper, SettingsHelper } from "../helpers";
import { Command } from "../viewpanel/Command";
@@ -9,6 +10,7 @@ import { Article } from '../commands';
import { TagType } from '../viewpanel/TagType';
import { TaxonomyType } from '../models';
import { exec } from 'child_process';
import * as path from 'path';
export class ExplorerView implements WebviewViewProvider, Disposable {
@@ -108,6 +110,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
case CommandToCode.openFile:
commands.executeCommand('revealFileInOS');
break;
case CommandToCode.runCustomScript:
this.runCustomScript(msg);
break;
case CommandToCode.openProject:
const wsFolders = workspace.workspaceFolders;
if (wsFolders && wsFolders.length > 0) {
@@ -162,6 +167,36 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
}
}
/**
* Run a custom script
* @param msg
*/
private runCustomScript(msg: { command: string, data: any}) {
const config = workspace.getConfiguration(CONFIG_KEY);
const scripts: CustomScript[] | undefined = config.get(SETTING_CUSTOM_SCRIPTS);
if (msg?.data?.title && msg?.data?.script && scripts && scripts.find((s: CustomScript) => s.title === msg?.data?.title)?.title) {
const editor = window.activeTextEditor;
const wsFolders = workspace.workspaceFolders;
if (wsFolders && wsFolders.length > 0) {
const wsPath = wsFolders[0].uri.fsPath;
exec(`node ${path.join(wsPath, msg.data.script)} "${wsPath}" "${editor?.document.uri.fsPath}"`, (error, stdout) => {
if (error) {
window.showErrorMessage(`${msg?.data?.title}: ${error.message}`);
return;
}
window.showInformationMessage(`${msg?.data?.title}: ${stdout || "Executed your custom script."}`, 'Copy output').then(value => {
if (value === 'Copy output') {
vscodeEnv.clipboard.writeText(stdout);
}
});
});
}
}
}
/**
* Retrieve the extension settings
*/
@@ -181,7 +216,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
},
tags: config.get(SETTING_TAXONOMY_TAGS) || [],
categories: config.get(SETTING_TAXONOMY_CATEGORIES) || [],
freeform: config.get(SETTING_PANEL_FREEFORM)
freeform: config.get(SETTING_PANEL_FREEFORM),
scripts: config.get(SETTING_CUSTOM_SCRIPTS)
} as PanelSettings
});
}