mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
Merge pull request #471 from tanhakabir/notebook-renderer
Notebook renderer examples
This commit is contained in:
22
notebook-renderer-react-sample/.eslintrc.json
Normal file
22
notebook-renderer-react-sample/.eslintrc.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/naming-convention": "warn",
|
||||
"@typescript-eslint/semi": "warn",
|
||||
"curly": "warn",
|
||||
"eqeqeq": "warn",
|
||||
"no-throw-literal": "warn",
|
||||
"semi": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
8
notebook-renderer-react-sample/.vscode/extensions.json
vendored
Normal file
8
notebook-renderer-react-sample/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"eamodio.tsl-problem-matcher"
|
||||
]
|
||||
}
|
||||
51
notebook-renderer-react-sample/.vscode/launch.json
vendored
Normal file
51
notebook-renderer-react-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "pwa-extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"debugWebviews": {
|
||||
"urlFilter": "*notebookRenderer*"
|
||||
},
|
||||
"trace": true,
|
||||
"preLaunchTask": "npm: dev"
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (no server)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"debugWebviews": true,
|
||||
"preLaunchTask": "npm: watch"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
notebook-renderer-react-sample/.vscode/settings.json
vendored
Normal file
11
notebook-renderer-react-sample/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
36
notebook-renderer-react-sample/.vscode/tasks.json
vendored
Normal file
36
notebook-renderer-react-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"problemMatcher": ["$tsc-watch", "$ts-checker-webpack-watch"],
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": ["$tsc-watch", "$ts-checker-webpack-watch"],
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "compile",
|
||||
"problemMatcher": ["$tsc", "$ts-checker-webpack"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
12
notebook-renderer-react-sample/.vscodeignore
Normal file
12
notebook-renderer-react-sample/.vscodeignore
Normal file
@ -0,0 +1,12 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/*.tsbuildinfo
|
||||
1
notebook-renderer-react-sample/.yarnrc
Normal file
1
notebook-renderer-react-sample/.yarnrc
Normal file
@ -0,0 +1 @@
|
||||
--ignore-engines true
|
||||
9
notebook-renderer-react-sample/CHANGELOG.md
Normal file
9
notebook-renderer-react-sample/CHANGELOG.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "notebook-renderer-react-sample" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
||||
28
notebook-renderer-react-sample/README.md
Normal file
28
notebook-renderer-react-sample/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# notebook-renderer-react-sample
|
||||
|
||||
⚠️ Work-in-progress starter code for custom notebook renderers in VS Code. Expect this to change as notebooks matures. ⚠️
|
||||
|
||||
This starter includes:
|
||||
|
||||
- 🖥️ TypeScript code to create a simple `NotebookOutputRenderer`
|
||||
- 📦 A Webpack build for renderer client code
|
||||
- ⚡ Support for hot module reloading and safe boilerplate
|
||||
- 🎨 CSS modules support
|
||||
|
||||
### Running this Sample
|
||||
|
||||
1. `cd notebook-renderer-react-sample`
|
||||
1. `code-insiders .`: Open the folder in VS Code Insiders
|
||||
1. Hit `F5` to build+debug
|
||||
|
||||
### Structure
|
||||
|
||||
A Notebook Renderer consists of code that runs in the VS Code Extension Host (Node.js), which registers the renderer and passes data into the UI code running inside a WebView (Browser/DOM).
|
||||
|
||||
This uses TypeScript project references. There are three projects in the `src` directory:
|
||||
|
||||
- `extension` contains the code running in Node.js extension host. It's compiled with `tsc`.
|
||||
- `client` is the UI code, built by Webpack, with access to the DOM.
|
||||
- `common` contains code shared between the extension and client.
|
||||
|
||||
When you run `watch`, `compile`, or `dev`, we invoke both `tsc` and `webpack` to compile the extension and the client portion of the code.
|
||||
66
notebook-renderer-react-sample/package.json
Normal file
66
notebook-renderer-react-sample/package.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "notebook-renderer-react-sample",
|
||||
"displayName": "notebook-renderer-react-sample",
|
||||
"description": "Example of a Notebook renderer (visualizer) for GitHub Issues Notebook built with React.",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.58.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension/extension.js",
|
||||
"contributes": {
|
||||
"notebookRenderer": [
|
||||
{
|
||||
"id": "notebook-renderer-react-github-issues",
|
||||
"entrypoint": "./out/client/index.js",
|
||||
"displayName": "GitHub Issues React-based Renderer",
|
||||
"mimeTypes": [
|
||||
"x-application/github-issues"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile && node out/test/checkNoTestProvider.js",
|
||||
"compile": "npm run compile:extension && npm run compile:client",
|
||||
"compile:extension": "tsc -b",
|
||||
"compile:client": "webpack --mode production",
|
||||
"lint": "eslint src --ext ts",
|
||||
"watch": "concurrently -r \"npm:watch:*\"",
|
||||
"watch:extension": "tsc -b --watch",
|
||||
"watch:client": "webpack --mode development --watch",
|
||||
"dev": "concurrently -r npm:watch:extension npm:watch:client",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/node": "14.x",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/vscode": "^1.58.0",
|
||||
"@types/vscode-notebook-renderer": "^1.57.8",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.26.0",
|
||||
"@typescript-eslint/parser": "^4.26.0",
|
||||
"concurrently": "^5.3.0",
|
||||
"css-loader": "^4.2.0",
|
||||
"eslint": "^7.27.0",
|
||||
"fork-ts-checker-webpack-plugin": "^5.0.14",
|
||||
"glob": "^7.1.7",
|
||||
"mocha": "^8.4.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^9.2.2",
|
||||
"typescript": "^4.3.2",
|
||||
"vscode-notebook-error-overlay": "^1.0.1",
|
||||
"vscode-test": "^1.5.2",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.0"
|
||||
}
|
||||
}
|
||||
4
notebook-renderer-react-sample/src/client/css.d.ts
vendored
Normal file
4
notebook-renderer-react-sample/src/client/css.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.css' {
|
||||
const classes: { [className: string]: string };
|
||||
export = classes;
|
||||
}
|
||||
40
notebook-renderer-react-sample/src/client/index.tsx
Normal file
40
notebook-renderer-react-sample/src/client/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './style.css';
|
||||
|
||||
import { IssuesList } from './render';
|
||||
import errorOverlay from 'vscode-notebook-error-overlay';
|
||||
import type { ActivationFunction } from 'vscode-notebook-renderer';
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// This is the entrypoint to the notebook renderer's webview client-side code.
|
||||
// This contains some boilerplate that calls the `render()` function when new
|
||||
// output is available. You probably don't need to change this code; put your
|
||||
// rendering logic inside of the `render()` function.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
export const activate: ActivationFunction = context => {
|
||||
return {
|
||||
renderOutputItem(outputItem, element) {
|
||||
let shadow = element.shadowRoot;
|
||||
if (!shadow) {
|
||||
shadow = element.attachShadow({ mode: 'open' });
|
||||
const root = document.createElement('div');
|
||||
root.id = 'root';
|
||||
shadow.append(root);
|
||||
}
|
||||
const root = shadow.querySelector<HTMLElement>('#root')!;
|
||||
errorOverlay.wrap(root, () => {
|
||||
root.innerHTML = '';
|
||||
const node = document.createElement('div');
|
||||
root.appendChild(node);
|
||||
|
||||
ReactDOM.render(<IssuesList info={{ container: node, mime: outputItem.mime, value: outputItem.json(), context }} />, root);
|
||||
});
|
||||
},
|
||||
disposeOutputItem(outputId) {
|
||||
// Do any teardown here. outputId is the cell output being deleted, or
|
||||
// undefined if we're clearing all outputs.
|
||||
}
|
||||
};
|
||||
};
|
||||
42
notebook-renderer-react-sample/src/client/render.tsx
Normal file
42
notebook-renderer-react-sample/src/client/render.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import React from "react";
|
||||
import type { RendererContext } from 'vscode-notebook-renderer';
|
||||
|
||||
interface IRenderInfo {
|
||||
container: HTMLElement;
|
||||
mime: string;
|
||||
value: GitHubIssuesValue[];
|
||||
context: RendererContext<unknown>;
|
||||
}
|
||||
|
||||
interface GitHubIssuesValue {
|
||||
title: string;
|
||||
url: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export const IssuesList: React.FC<{info: IRenderInfo}> = ({info}) => {
|
||||
const issues = info.value.map(item => {
|
||||
return <tr>
|
||||
<td><a href={item.url}>{item.title}</a></td>
|
||||
<td>{item.body}</td>
|
||||
</tr>;
|
||||
});
|
||||
|
||||
return <div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Issue</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
{issues}
|
||||
</table>
|
||||
</div>;
|
||||
};
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.addDisposeHandler(() => {
|
||||
// In development, this will be called before the renderer is reloaded. You
|
||||
// can use this to clean up or stash any state.
|
||||
});
|
||||
}
|
||||
5
notebook-renderer-react-sample/src/client/style.css
Normal file
5
notebook-renderer-react-sample/src/client/style.css
Normal file
@ -0,0 +1,5 @@
|
||||
.json code {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: lightblue;
|
||||
}
|
||||
13
notebook-renderer-react-sample/src/client/tsconfig.json
Normal file
13
notebook-renderer-react-sample/src/client/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
// noEmit prevents the default tsc from building this--we use webpack instead
|
||||
"noEmit": true,
|
||||
"rootDir": ".",
|
||||
"module": "esnext",
|
||||
"lib": ["ES2019", "dom"],
|
||||
"types": ["webpack-env", "vscode-notebook-renderer"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
13
notebook-renderer-react-sample/src/extension/extension.ts
Normal file
13
notebook-renderer-react-sample/src/extension/extension.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
// This method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
export function deactivate() { }
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out/extension",
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
129
notebook-renderer-react-sample/src/test/checkNoTestProvider.ts
Normal file
129
notebook-renderer-react-sample/src/test/checkNoTestProvider.ts
Normal file
@ -0,0 +1,129 @@
|
||||
// This script checks that the test provider isn't included in the package.json
|
||||
// or extension.ts. The extension provider is given as a tool for development,
|
||||
// but should not be published to the marketplace.
|
||||
//
|
||||
// This script is not super comprehensive, it's just here to prevent simple mistakes.
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
||||
import { EOL } from 'os';
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const packageJson = require('../../package.json');
|
||||
|
||||
class DetectedError extends Error {
|
||||
constructor(message: string, public readonly fix: () => void) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
const checkNotInExtensionTs = () => {
|
||||
const entrypoint = path.resolve(rootDir, 'src', 'extension', 'extension.ts');
|
||||
if (!existsSync(entrypoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = readFileSync(entrypoint, 'utf-8');
|
||||
const program = ts.createSourceFile(
|
||||
path.basename(entrypoint),
|
||||
contents,
|
||||
ts.ScriptTarget.ESNext,
|
||||
true,
|
||||
);
|
||||
|
||||
const removeRegistration = (node: ts.Node) => () => writeFileSync(
|
||||
entrypoint,
|
||||
contents.slice(0, node.pos) + contents.slice(node.end + +(contents[node.end] === ',')),
|
||||
);
|
||||
|
||||
ts.forEachChild(program, function walk(node: ts.Node) {
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
/(^|\W)registerNotebookContentProvider$/.test(node.expression.getText()) &&
|
||||
node.arguments[1]?.getText().includes('SampleContentProvider')
|
||||
) {
|
||||
throw new DetectedError(
|
||||
'`registerNotebookContentProvider()` is still called with the SampleContentProvider.',
|
||||
removeRegistration(node),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
/(^|\W)registerNotebookKernel$/.test(node.expression.getText()) &&
|
||||
node.arguments[2]?.getText().includes('SampleKernel')
|
||||
) {
|
||||
throw new DetectedError(
|
||||
'`registerNotebookKernel()` is still called with the TestKernel.',
|
||||
removeRegistration(node),
|
||||
);
|
||||
}
|
||||
|
||||
ts.forEachChild(node, walk);
|
||||
});
|
||||
};
|
||||
|
||||
const checkNotInPackageJson = () => {
|
||||
const providers = packageJson.contributes?.notebookProvider ?? [];
|
||||
const testIndex = providers.findIndex(
|
||||
(p: { viewType: string }) => p.viewType === 'test-notebook-renderer',
|
||||
);
|
||||
if (testIndex !== -1) {
|
||||
throw new DetectedError(
|
||||
`The "test-notebook-renderer" is still registered in the contributes section of your package.json.`,
|
||||
() => {
|
||||
providers.splice(testIndex, 1);
|
||||
writeFileSync(
|
||||
path.resolve(rootDir, 'package.json'),
|
||||
JSON.stringify(packageJson, null, 2) + EOL,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
(() => {
|
||||
if (process.argv.includes('--fix')) {
|
||||
while (true) {
|
||||
try {
|
||||
checkNotInPackageJson();
|
||||
checkNotInExtensionTs();
|
||||
} catch (e) {
|
||||
if (!(e instanceof DetectedError)) {
|
||||
throw e;
|
||||
} else {
|
||||
e.fix();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return console.log('Test provider removed!');
|
||||
}
|
||||
}
|
||||
|
||||
let errors: DetectedError[] = [];
|
||||
for (const check of [checkNotInPackageJson, checkNotInExtensionTs]) {
|
||||
try {
|
||||
check();
|
||||
} catch (e) {
|
||||
if (!(e instanceof DetectedError)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!errors.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const execPath = path.relative(process.cwd(), __filename);
|
||||
console.error(errors.map((e) => e.message).join(' '));
|
||||
console.error('');
|
||||
console.error(
|
||||
'You should remove the test provider before publishing your extension to avoid ' +
|
||||
`conflicts. To fix this automatically, run \`node ${execPath} --fix\``,
|
||||
);
|
||||
})();
|
||||
23
notebook-renderer-react-sample/src/test/runTest.ts
Normal file
23
notebook-renderer-react-sample/src/test/runTest.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { runTests } from 'vscode-test';
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@ -0,0 +1,15 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
37
notebook-renderer-react-sample/src/test/suite/index.ts
Normal file
37
notebook-renderer-react-sample/src/test/suite/index.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
});
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
8
notebook-renderer-react-sample/src/test/tsconfig.json
Normal file
8
notebook-renderer-react-sample/src/test/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out/test",
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
17
notebook-renderer-react-sample/src/tsconfig-base.json
Normal file
17
notebook-renderer-react-sample/src/tsconfig-base.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2019",
|
||||
"lib": [
|
||||
"ES2019"
|
||||
],
|
||||
"types": ["node"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
}
|
||||
}
|
||||
14
notebook-renderer-react-sample/tsconfig.json
Normal file
14
notebook-renderer-react-sample/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./src/client"
|
||||
},
|
||||
{
|
||||
"path": "./src/test"
|
||||
},
|
||||
{
|
||||
"path": "./src/extension"
|
||||
}
|
||||
]
|
||||
}
|
||||
79
notebook-renderer-react-sample/webpack.config.js
Normal file
79
notebook-renderer-react-sample/webpack.config.js
Normal file
@ -0,0 +1,79 @@
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const { DefinePlugin } = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const outputFilename = 'index.js';
|
||||
const devServerPort = 8111;
|
||||
|
||||
module.exports = (env, argv) => ({
|
||||
mode: argv.mode,
|
||||
devtool: argv.mode === 'production' ? false : 'inline-source-map',
|
||||
entry: './src/client/index.tsx',
|
||||
output: {
|
||||
path: path.join(__dirname, 'out', 'client'),
|
||||
filename: outputFilename,
|
||||
publicPath: '',
|
||||
libraryTarget: 'module',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.css'],
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Allow importing ts(x) files:
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: 'src/client/tsconfig.json',
|
||||
// transpileOnly enables hot-module-replacement
|
||||
transpileOnly: true,
|
||||
compilerOptions: {
|
||||
// Overwrite the noEmit from the client's tsconfig
|
||||
noEmit: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Allow importing CSS modules:
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
port: devServerPort,
|
||||
hot: true,
|
||||
// Disable the host check, otherwise the bundle running in VS Code won't be
|
||||
// able to connect to the dev server
|
||||
disableHostCheck: true,
|
||||
writeToDisk: true,
|
||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||
},
|
||||
plugins: [
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
tsconfig: 'src/client/tsconfig.json',
|
||||
},
|
||||
}),
|
||||
new DefinePlugin({
|
||||
// Path from the output filename to the output directory
|
||||
__webpack_relative_entrypoint_to_root__: JSON.stringify(
|
||||
path.posix.relative(path.posix.dirname(`/${outputFilename}`), '/'),
|
||||
),
|
||||
scriptUrl: 'import.meta.url',
|
||||
}),
|
||||
],
|
||||
});
|
||||
2884
notebook-renderer-react-sample/yarn.lock
Normal file
2884
notebook-renderer-react-sample/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
22
notebook-renderer-sample/.eslintrc.json
Normal file
22
notebook-renderer-sample/.eslintrc.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/naming-convention": "warn",
|
||||
"@typescript-eslint/semi": "warn",
|
||||
"curly": "warn",
|
||||
"eqeqeq": "warn",
|
||||
"no-throw-literal": "warn",
|
||||
"semi": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
8
notebook-renderer-sample/.vscode/extensions.json
vendored
Normal file
8
notebook-renderer-sample/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"eamodio.tsl-problem-matcher"
|
||||
]
|
||||
}
|
||||
51
notebook-renderer-sample/.vscode/launch.json
vendored
Normal file
51
notebook-renderer-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "pwa-extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"debugWebviews": {
|
||||
"urlFilter": "*notebookRenderer*"
|
||||
},
|
||||
"trace": true,
|
||||
"preLaunchTask": "npm: dev"
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (no server)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"debugWebviews": true,
|
||||
"preLaunchTask": "npm: watch"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
notebook-renderer-sample/.vscode/settings.json
vendored
Normal file
11
notebook-renderer-sample/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
36
notebook-renderer-sample/.vscode/tasks.json
vendored
Normal file
36
notebook-renderer-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"problemMatcher": ["$tsc-watch", "$ts-checker-webpack-watch"],
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": ["$tsc-watch", "$ts-checker-webpack-watch"],
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "compile",
|
||||
"problemMatcher": ["$tsc", "$ts-checker-webpack"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
12
notebook-renderer-sample/.vscodeignore
Normal file
12
notebook-renderer-sample/.vscodeignore
Normal file
@ -0,0 +1,12 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/*.tsbuildinfo
|
||||
1
notebook-renderer-sample/.yarnrc
Normal file
1
notebook-renderer-sample/.yarnrc
Normal file
@ -0,0 +1 @@
|
||||
--ignore-engines true
|
||||
9
notebook-renderer-sample/CHANGELOG.md
Normal file
9
notebook-renderer-sample/CHANGELOG.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "notebook-renderer-sample" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
||||
28
notebook-renderer-sample/README.md
Normal file
28
notebook-renderer-sample/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# notebook-renderer-sample
|
||||
|
||||
⚠️ Work-in-progress starter code for custom notebook renderers in VS Code. Expect this to change as notebooks matures. ⚠️
|
||||
|
||||
This starter includes:
|
||||
|
||||
- 🖥️ TypeScript code to create a simple `NotebookOutputRenderer`
|
||||
- 📦 A Webpack build for renderer client code
|
||||
- ⚡ Support for hot module reloading and safe boilerplate
|
||||
- 🎨 CSS modules support
|
||||
|
||||
### Running this Sample
|
||||
|
||||
1. `cd notebook-renderer-sample`
|
||||
1. `code-insiders .`: Open the folder in VS Code Insiders
|
||||
1. Hit `F5` to build+debug
|
||||
|
||||
### Structure
|
||||
|
||||
A Notebook Renderer consists of code that runs in the VS Code Extension Host (Node.js), which registers the renderer and passes data into the UI code running inside a WebView (Browser/DOM).
|
||||
|
||||
This uses TypeScript project references. There are three projects in the `src` directory:
|
||||
|
||||
- `extension` contains the code running in Node.js extension host. It's compiled with `tsc`.
|
||||
- `client` is the UI code, built by Webpack, with access to the DOM.
|
||||
- `common` contains code shared between the extension and client.
|
||||
|
||||
When you run `watch`, `compile`, or `dev`, we invoke both `tsc` and `webpack` to compile the extension and the client portion of the code.
|
||||
60
notebook-renderer-sample/package.json
Normal file
60
notebook-renderer-sample/package.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "notebook-renderer-sample",
|
||||
"displayName": "notebook-renderer-sample",
|
||||
"description": "Example of a Notebook renderer (visualizer) for GitHub Issues Notebook that doesn't use any frontend frameworks.",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.58.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension/extension.js",
|
||||
"contributes": {
|
||||
"notebookRenderer": [
|
||||
{
|
||||
"id": "notebook-renderer-github-issues",
|
||||
"entrypoint": "./out/client/index.js",
|
||||
"displayName": "GitHub Issues Plain Renderer",
|
||||
"mimeTypes": ["x-application/github-issues"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile && node out/test/checkNoTestProvider.js",
|
||||
"compile": "npm run compile:extension && npm run compile:client",
|
||||
"compile:extension": "tsc -b",
|
||||
"compile:client": "webpack --mode production",
|
||||
"lint": "eslint src --ext ts",
|
||||
"watch": "concurrently -r \"npm:watch:*\"",
|
||||
"watch:extension": "tsc -b --watch",
|
||||
"watch:client": "webpack --mode development --watch",
|
||||
"dev": "concurrently -r npm:watch:extension npm:watch:client",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/node": "14.x",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.26.0",
|
||||
"@typescript-eslint/parser": "^4.26.0",
|
||||
"@types/vscode-notebook-renderer": "^1.57.8",
|
||||
"@types/vscode": "^1.58.0",
|
||||
"concurrently": "^5.3.0",
|
||||
"css-loader": "^4.2.0",
|
||||
"eslint": "^7.27.0",
|
||||
"fork-ts-checker-webpack-plugin": "^5.0.14",
|
||||
"glob": "^7.1.7",
|
||||
"mocha": "^8.4.0",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^9.2.2",
|
||||
"typescript": "^4.3.2",
|
||||
"vscode-notebook-error-overlay": "^1.0.1",
|
||||
"vscode-test": "^1.5.2",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.0"
|
||||
}
|
||||
}
|
||||
4
notebook-renderer-sample/src/client/css.d.ts
vendored
Normal file
4
notebook-renderer-sample/src/client/css.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.css' {
|
||||
const classes: { [className: string]: string };
|
||||
export = classes;
|
||||
}
|
||||
42
notebook-renderer-sample/src/client/index.ts
Normal file
42
notebook-renderer-sample/src/client/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { render } from './render';
|
||||
import errorOverlay from 'vscode-notebook-error-overlay';
|
||||
import type { ActivationFunction } from 'vscode-notebook-renderer';
|
||||
|
||||
// Fix the public path so that any async import()'s work as expected.
|
||||
declare const __webpack_relative_entrypoint_to_root__: string;
|
||||
declare const scriptUrl: string;
|
||||
|
||||
__webpack_public_path__ = new URL(scriptUrl.replace(/[^/]+$/, '') + __webpack_relative_entrypoint_to_root__).toString();
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// This is the entrypoint to the notebook renderer's webview client-side code.
|
||||
// This contains some boilerplate that calls the `render()` function when new
|
||||
// output is available. You probably don't need to change this code; put your
|
||||
// rendering logic inside of the `render()` function.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
export const activate: ActivationFunction = context => {
|
||||
return {
|
||||
renderOutputItem(outputItem, element) {
|
||||
let shadow = element.shadowRoot;
|
||||
if (!shadow) {
|
||||
shadow = element.attachShadow({ mode: 'open' });
|
||||
const root = document.createElement('div');
|
||||
root.id = 'root';
|
||||
shadow.append(root);
|
||||
}
|
||||
const root = shadow.querySelector<HTMLElement>('#root')!;
|
||||
errorOverlay.wrap(root, () => {
|
||||
root.innerHTML = '';
|
||||
const node = document.createElement('div');
|
||||
root.appendChild(node);
|
||||
|
||||
render({ container: node, mime: outputItem.mime, value: outputItem.json(), context });
|
||||
});
|
||||
},
|
||||
disposeOutputItem(outputId) {
|
||||
// Do any teardown here. outputId is the cell output being deleted, or
|
||||
// undefined if we're clearing all outputs.
|
||||
}
|
||||
};
|
||||
};
|
||||
66
notebook-renderer-sample/src/client/render.ts
Normal file
66
notebook-renderer-sample/src/client/render.ts
Normal file
@ -0,0 +1,66 @@
|
||||
// We've set up this sample using CSS modules, which lets you import class
|
||||
// names into JavaScript: https://github.com/css-modules/css-modules
|
||||
// You can configure or change this in the webpack.config.js file.
|
||||
import * as style from './style.css';
|
||||
import type { RendererContext } from 'vscode-notebook-renderer';
|
||||
|
||||
interface IRenderInfo {
|
||||
container: HTMLElement;
|
||||
mime: string;
|
||||
value: GitHubIssuesValue[];
|
||||
context: RendererContext<unknown>;
|
||||
}
|
||||
interface GitHubIssuesValue {
|
||||
title: string;
|
||||
url: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
// This function is called to render your contents.
|
||||
export function render({ container, mime, value }: IRenderInfo) {
|
||||
// Format the JSON and insert it as <pre><code>{ ... }</code></pre>
|
||||
// Replace this with your custom code!
|
||||
const pre = document.createElement('pre');
|
||||
pre.classList.add(style.json);
|
||||
|
||||
// Create a simple table with issue titles and links
|
||||
const table = document.createElement('table');
|
||||
table.className = 'issues-list';
|
||||
const headerRow = document.createElement('tr');
|
||||
const tableHeaders = ['Issue', 'Description'];
|
||||
|
||||
tableHeaders.forEach(label => {
|
||||
const header = document.createElement('th');
|
||||
header.textContent = label;
|
||||
headerRow.appendChild(header);
|
||||
});
|
||||
|
||||
table.appendChild(headerRow);
|
||||
|
||||
value.forEach(item => {
|
||||
const row = document.createElement('tr');
|
||||
|
||||
const title = document.createElement('td');
|
||||
const link = document.createElement('a');
|
||||
link.href = item.url;
|
||||
link.textContent = item.title;
|
||||
title.appendChild(link);
|
||||
row.appendChild(title);
|
||||
|
||||
const body = document.createElement('td');
|
||||
body.textContent = item.body;
|
||||
row.appendChild(body);
|
||||
|
||||
table.appendChild(row);
|
||||
});
|
||||
|
||||
pre.appendChild(table);
|
||||
container.appendChild(pre);
|
||||
}
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.addDisposeHandler(() => {
|
||||
// In development, this will be called before the renderer is reloaded. You
|
||||
// can use this to clean up or stash any state.
|
||||
});
|
||||
}
|
||||
3
notebook-renderer-sample/src/client/style.css
Normal file
3
notebook-renderer-sample/src/client/style.css
Normal file
@ -0,0 +1,3 @@
|
||||
.issues-list {
|
||||
width: 100px;
|
||||
}
|
||||
11
notebook-renderer-sample/src/client/tsconfig.json
Normal file
11
notebook-renderer-sample/src/client/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
// noEmit prevents the default tsc from building this--we use webpack instead
|
||||
"noEmit": true,
|
||||
"rootDir": ".",
|
||||
"module": "esnext",
|
||||
"lib": ["ES2019", "dom"],
|
||||
"types": ["webpack-env", "vscode-notebook-renderer"],
|
||||
}
|
||||
}
|
||||
13
notebook-renderer-sample/src/extension/extension.ts
Normal file
13
notebook-renderer-sample/src/extension/extension.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
// This method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
export function deactivate() { }
|
||||
8
notebook-renderer-sample/src/extension/tsconfig.json
Normal file
8
notebook-renderer-sample/src/extension/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out/extension",
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
129
notebook-renderer-sample/src/test/checkNoTestProvider.ts
Normal file
129
notebook-renderer-sample/src/test/checkNoTestProvider.ts
Normal file
@ -0,0 +1,129 @@
|
||||
// This script checks that the test provider isn't included in the package.json
|
||||
// or extension.ts. The extension provider is given as a tool for development,
|
||||
// but should not be published to the marketplace.
|
||||
//
|
||||
// This script is not super comprehensive, it's just here to prevent simple mistakes.
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
||||
import { EOL } from 'os';
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const packageJson = require('../../package.json');
|
||||
|
||||
class DetectedError extends Error {
|
||||
constructor(message: string, public readonly fix: () => void) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
const checkNotInExtensionTs = () => {
|
||||
const entrypoint = path.resolve(rootDir, 'src', 'extension', 'extension.ts');
|
||||
if (!existsSync(entrypoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = readFileSync(entrypoint, 'utf-8');
|
||||
const program = ts.createSourceFile(
|
||||
path.basename(entrypoint),
|
||||
contents,
|
||||
ts.ScriptTarget.ESNext,
|
||||
true,
|
||||
);
|
||||
|
||||
const removeRegistration = (node: ts.Node) => () => writeFileSync(
|
||||
entrypoint,
|
||||
contents.slice(0, node.pos) + contents.slice(node.end + +(contents[node.end] === ',')),
|
||||
);
|
||||
|
||||
ts.forEachChild(program, function walk(node: ts.Node) {
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
/(^|\W)registerNotebookContentProvider$/.test(node.expression.getText()) &&
|
||||
node.arguments[1]?.getText().includes('SampleContentProvider')
|
||||
) {
|
||||
throw new DetectedError(
|
||||
'`registerNotebookContentProvider()` is still called with the SampleContentProvider.',
|
||||
removeRegistration(node),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
/(^|\W)registerNotebookKernel$/.test(node.expression.getText()) &&
|
||||
node.arguments[2]?.getText().includes('SampleKernel')
|
||||
) {
|
||||
throw new DetectedError(
|
||||
'`registerNotebookKernel()` is still called with the TestKernel.',
|
||||
removeRegistration(node),
|
||||
);
|
||||
}
|
||||
|
||||
ts.forEachChild(node, walk);
|
||||
});
|
||||
};
|
||||
|
||||
const checkNotInPackageJson = () => {
|
||||
const providers = packageJson.contributes?.notebookProvider ?? [];
|
||||
const testIndex = providers.findIndex(
|
||||
(p: { viewType: string }) => p.viewType === 'test-notebook-renderer',
|
||||
);
|
||||
if (testIndex !== -1) {
|
||||
throw new DetectedError(
|
||||
`The "test-notebook-renderer" is still registered in the contributes section of your package.json.`,
|
||||
() => {
|
||||
providers.splice(testIndex, 1);
|
||||
writeFileSync(
|
||||
path.resolve(rootDir, 'package.json'),
|
||||
JSON.stringify(packageJson, null, 2) + EOL,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
(() => {
|
||||
if (process.argv.includes('--fix')) {
|
||||
while (true) {
|
||||
try {
|
||||
checkNotInPackageJson();
|
||||
checkNotInExtensionTs();
|
||||
} catch (e) {
|
||||
if (!(e instanceof DetectedError)) {
|
||||
throw e;
|
||||
} else {
|
||||
e.fix();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return console.log('Test provider removed!');
|
||||
}
|
||||
}
|
||||
|
||||
let errors: DetectedError[] = [];
|
||||
for (const check of [checkNotInPackageJson, checkNotInExtensionTs]) {
|
||||
try {
|
||||
check();
|
||||
} catch (e) {
|
||||
if (!(e instanceof DetectedError)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!errors.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const execPath = path.relative(process.cwd(), __filename);
|
||||
console.error(errors.map((e) => e.message).join(' '));
|
||||
console.error('');
|
||||
console.error(
|
||||
'You should remove the test provider before publishing your extension to avoid ' +
|
||||
`conflicts. To fix this automatically, run \`node ${execPath} --fix\``,
|
||||
);
|
||||
})();
|
||||
23
notebook-renderer-sample/src/test/runTest.ts
Normal file
23
notebook-renderer-sample/src/test/runTest.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { runTests } from 'vscode-test';
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
15
notebook-renderer-sample/src/test/suite/extension.test.ts
Normal file
15
notebook-renderer-sample/src/test/suite/extension.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
37
notebook-renderer-sample/src/test/suite/index.ts
Normal file
37
notebook-renderer-sample/src/test/suite/index.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
});
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
8
notebook-renderer-sample/src/test/tsconfig.json
Normal file
8
notebook-renderer-sample/src/test/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out/test",
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
0
notebook-renderer-sample/src/test/types/.gitkeep
Normal file
0
notebook-renderer-sample/src/test/types/.gitkeep
Normal file
17
notebook-renderer-sample/src/tsconfig-base.json
Normal file
17
notebook-renderer-sample/src/tsconfig-base.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2019",
|
||||
"lib": [
|
||||
"ES2019"
|
||||
],
|
||||
"types": ["node"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
}
|
||||
}
|
||||
14
notebook-renderer-sample/tsconfig.json
Normal file
14
notebook-renderer-sample/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./src/client"
|
||||
},
|
||||
{
|
||||
"path": "./src/test"
|
||||
},
|
||||
{
|
||||
"path": "./src/extension"
|
||||
}
|
||||
]
|
||||
}
|
||||
79
notebook-renderer-sample/webpack.config.js
Normal file
79
notebook-renderer-sample/webpack.config.js
Normal file
@ -0,0 +1,79 @@
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const { DefinePlugin } = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const outputFilename = 'index.js';
|
||||
const devServerPort = 8111;
|
||||
|
||||
module.exports = (env, argv) => ({
|
||||
mode: argv.mode,
|
||||
devtool: argv.mode === 'production' ? false : 'inline-source-map',
|
||||
entry: './src/client/index.ts',
|
||||
output: {
|
||||
path: path.join(__dirname, 'out', 'client'),
|
||||
filename: outputFilename,
|
||||
publicPath: '',
|
||||
libraryTarget: 'module',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.css'],
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Allow importing ts(x) files:
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: 'src/client/tsconfig.json',
|
||||
// transpileOnly enables hot-module-replacement
|
||||
transpileOnly: true,
|
||||
compilerOptions: {
|
||||
// Overwrite the noEmit from the client's tsconfig
|
||||
noEmit: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Allow importing CSS modules:
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
port: devServerPort,
|
||||
hot: true,
|
||||
// Disable the host check, otherwise the bundle running in VS Code won't be
|
||||
// able to connect to the dev server
|
||||
disableHostCheck: true,
|
||||
writeToDisk: true,
|
||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||
},
|
||||
plugins: [
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
tsconfig: 'src/client/tsconfig.json',
|
||||
},
|
||||
}),
|
||||
new DefinePlugin({
|
||||
// Path from the output filename to the output directory
|
||||
__webpack_relative_entrypoint_to_root__: JSON.stringify(
|
||||
path.posix.relative(path.posix.dirname(`/${outputFilename}`), '/'),
|
||||
),
|
||||
scriptUrl: 'import.meta.url',
|
||||
}),
|
||||
],
|
||||
});
|
||||
2763
notebook-renderer-sample/yarn.lock
Normal file
2763
notebook-renderer-sample/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user