Skip to content

Commit

Permalink
Merge pull request #1249 from posit-dev/sagerb-add-webviewview-vue-sk…
Browse files Browse the repository at this point in the history
…eleton

Shell for Home View WebViewView
  • Loading branch information
sagerb authored Apr 1, 2024
2 parents 0808c41 + e95d335 commit 78bf6ea
Show file tree
Hide file tree
Showing 21 changed files with 1,957 additions and 23 deletions.
23 changes: 18 additions & 5 deletions extensions/vscode/.vscodeignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
.vscode/**
.vscode-test/**
src/**
.gitignore
.yarnrc
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts
**/_.map
\*\*/_.ts
CONTRIBUTING.md
vsc-extension-quickstart.md

# Ignore all webview files except the build directories

webviews/homeView/src/**
webviews/homeView/index.html
webviews/homeView/package.json
webviews/homeView/package-lock.json
webviews/homeView/README.md
webviews/homeView/node_modules/**

# Ignore configuration files

**/.gitignore
**/.eslintrc.json
**/tsconfig\*.json
**/vite.config.json
3 changes: 3 additions & 0 deletions extensions/vscode/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ clean:
rm -rf dist
rm -rf node_modules
rm -rf out
just ./webviews/homeView/clean


# Install dependencies
deps:
Expand All @@ -68,6 +70,7 @@ deps:
else
npm install --no-audit --no-fund | sed 's/^/debug: /'
fi
just ./webviews/homeView/deps


configure os="$(just ../../os)" arch="$(just ../../arch)":
Expand Down
12 changes: 11 additions & 1 deletion extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,14 @@
"contextualTitle": "Publisher",
"when": "workbenchState == empty || workbenchState == workspace || posit.publish.missing || posit.publish.state == 'uninitialized'"
},
{
"id": "posit.publisher.homeView",
"type": "webview",
"name": "Home",
"contextualTitle": "Publisher",
"icon": "$(symbol-file)",
"when": "workbenchState == folder && !posit.publish.missing && posit.publish.state == 'initialized'"
},
{
"id": "posit.publisher.files",
"name": "Deployment Files",
Expand Down Expand Up @@ -451,7 +459,9 @@
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"compile": "npm run compile-extension && npm run compile-home-view",
"compile-extension": "tsc -p ./",
"compile-home-view": "npm run --prefix webviews/homeView compile",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile",
"lint": "eslint src --ext ts",
Expand Down
2 changes: 2 additions & 0 deletions extensions/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { FilesTreeDataProvider } from "./views/files";
import { RequirementsTreeDataProvider } from "./views/requirements";
import { CredentialsTreeDataProvider } from "./views/credentials";
import { HelpAndFeedbackTreeDataProvider } from "./views/helpAndFeedback";
import { HomeViewProvider } from "./views/homeView";
import { LogsTreeDataProvider } from "./views/logs";
import { EventStream } from "./events";

Expand Down Expand Up @@ -97,6 +98,7 @@ export async function activate(context: vscode.ExtensionContext) {
new CredentialsTreeDataProvider(apiReady).register(context);
new HelpAndFeedbackTreeDataProvider().register(context);
new LogsTreeDataProvider(stream).register(context);
new HomeViewProvider(context.extensionUri).register(context);

await service.start();

Expand Down
19 changes: 3 additions & 16 deletions extensions/vscode/src/panels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import * as vscode from "vscode";

import { getNonce } from "./utils/getNonce";

const DEFAULT_COLUMN = vscode.ViewColumn.Beside;

export class Panel {
Expand Down Expand Up @@ -86,7 +88,7 @@ export class Panel {
* @returns {string}
*/
export const createHTML = (url: string, webview: vscode.Webview): string => {
const nonce = createNonce();
const nonce = getNonce();
return (
// install https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html to enable code highlighting below
/*html*/
Expand Down Expand Up @@ -136,18 +138,3 @@ export const createContentSecurityPolicyContent = (
.join(" ");
return `default-src 'none'; ${content}`;
};

/**
* Creates a unique nonce value.
*
* @returns {string}
*/
const createNonce = (): string => {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
19 changes: 19 additions & 0 deletions extensions/vscode/src/utils/getNonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2024 by Posit Software, PBC.

/**
* A helper function that returns a unique alphanumeric identifier called a nonce.
*
* @remarks This function is primarily used to help enforce content security
* policies for resources/scripts being executed in a webview context.
*
* @returns A nonce
*/
export function getNonce() {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
22 changes: 22 additions & 0 deletions extensions/vscode/src/utils/getUri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) 2024 by Posit Software, PBC.

import { Uri, Webview } from "vscode";

/**
* A helper function which will get the webview URI of a given file or resource.
*
* @remarks This URI can be used within a webview's HTML as a link to the
* given file/resource.
*
* @param webview A reference to the extension webview
* @param extensionUri The URI of the directory containing the extension
* @param pathList An array of strings representing the path to a file/resource
* @returns A URI pointing to the file/resource
*/
export function getUri(
webview: Webview,
extensionUri: Uri,
pathList: string[],
) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList));
}
129 changes: 129 additions & 0 deletions extensions/vscode/src/views/homeView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (C) 2024 by Posit Software, PBC.

import {
Disposable,
Webview,
window,
Uri,
WebviewViewProvider,
WebviewView,
WebviewViewResolveContext,
CancellationToken,
ExtensionContext,
} from "vscode";
import { getUri } from "../utils/getUri";
import { getNonce } from "../utils/getNonce";

const viewName = "posit.publisher.homeView";

export class HomeViewProvider implements WebviewViewProvider {
private _disposables: Disposable[] = [];

constructor(private readonly _extensionUri: Uri) {}

public resolveWebviewView(
webviewView: WebviewView,
_: WebviewViewResolveContext,
_token: CancellationToken,
) {
// Allow scripts in the webview
webviewView.webview.options = {
// Enable JavaScript in the webview
enableScripts: true,
// Restrict the webview to only load resources from the `out` directory
localResourceRoots: [
Uri.joinPath(this._extensionUri, "out", "webviews", "homeView"),
],
};

// Set the HTML content that will fill the webview view
webviewView.webview.html = this._getWebviewContent(
webviewView.webview,
this._extensionUri,
);
}
/**
* Defines and returns the HTML that should be rendered within the webview panel.
*
* @remarks This is also the place where references to the Vue webview build files
* are created and inserted into the webview HTML.
*
* @param webview A reference to the extension webview
* @param extensionUri The URI of the directory containing the extension
* @returns A template string literal containing the HTML that should be
* rendered within the webview panel
*/
private _getWebviewContent(webview: Webview, extensionUri: Uri) {
// The CSS files from the Vue build output
const stylesUri = getUri(webview, extensionUri, [
"out",
"webviews",
"homeView",
"index.css",
]);
// const codiconsUri = webview.asWebviewUri(Uri.joinPath(extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css'));
// The JS file from the Vue build output
const scriptUri = getUri(webview, extensionUri, [
"out",
"webviews",
"homeView",
"index.js",
]);
// The codicon css (and related tff file) are needing to be loaded for icons
const codiconsUri = getUri(webview, extensionUri, [
"out",
"webviews",
"homeView",
"codicon.css",
]);

const nonce = getNonce();

// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
return /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy"
content="
default-src 'none';
font-src ${webview.cspSource};
style-src ${webview.cspSource} 'unsafe-inline';
script-src 'nonce-${nonce}';"
/>
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link rel="stylesheet" type="text/css" href="${codiconsUri}">
<title>Hello World</title>
</head>
<body>
<div id="app"></div>
<script type="module" nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>
`;
}

/**
* Cleans up and disposes of webview resources when view is disposed
*/
public dispose() {
// Dispose of all disposables (i.e. commands) for the current webview panel
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {
disposable.dispose();
}
}
}

public register(context: ExtensionContext) {
const provider = window.registerWebviewViewProvider(viewName, this, {
webviewOptions: {
retainContextWhenHidden: true,
},
});
context.subscriptions.push(provider);
}
}
3 changes: 2 additions & 1 deletion extensions/vscode/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true
}
},
"exclude": ["node_modules", "webviews"]
}
2 changes: 2 additions & 0 deletions extensions/vscode/webviews/homeView/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.vscode/*
10 changes: 10 additions & 0 deletions extensions/vscode/webviews/homeView/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# `webview-ui` Directory

This directory contains all of the code that will be executed within the webview context. It can be thought of as the place where all the "frontend" code of a webview is contained.

Types of content that can be contained here:

- Frontend framework code (i.e. Vue, SolidJS, React, Svelte, etc.)
- JavaScript files
- CSS files
- Assets / resources (i.e. images, illustrations, etc.)
12 changes: 12 additions & 0 deletions extensions/vscode/webviews/homeView/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Required for Vite only</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
32 changes: 32 additions & 0 deletions extensions/vscode/webviews/homeView/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
alias c := clean

_ci := env_var_or_default("CI", "false")

_debug := env_var_or_default("DEBUG", "false")

_with_debug := if _debug == "true" {
"set -x pipefail"
} else {
""
}

# Deletes ephemeral project files (i.e., cleans the project).
clean:
#!/usr/bin/env bash
set -eou pipefail
{{ _with_debug }}
rm -rf node_modules


# Install dependencies
deps:
#!/usr/bin/env bash
set -eou pipefail
{{ _with_debug }}
if [ {{ _ci }} = "true" ]; then
npm ci --no-audit --no-fund | sed 's/^/debug: /'
else
npm install --no-audit --no-fund | sed 's/^/debug: /'
fi
Loading

0 comments on commit 78bf6ea

Please sign in to comment.