diff --git a/.gitignore b/.gitignore index 875bff955c..b23b47a7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,9 @@ buildSrc/.gradle /stf/.project /stf.test/.project .DS_Store -/.gradle/ +docs/_site +docs/Gemfile.lock +.gradle/ /build/ # Common @@ -57,3 +59,14 @@ buildSrc/.gradle # LSP lsp/.classpath lsp/build + +# VS Code +vscode*/.classpath +vscode*/.project +vscode*/build +vscode*/package-lock.json +vscode*/.vscode +*.vscode +*.code-workspace +*.vscodeignore +dist/ diff --git a/build.gradle b/build.gradle index 6624db3846..f195b74e1a 100644 --- a/build.gradle +++ b/build.gradle @@ -196,6 +196,34 @@ task sarosLsp(type: Copy, dependsOn: [ into 'build/distribution/lsp' } +task sarosVSCode(type: Copy, dependsOn: [ + 'sarosLsp', + ':saros.vscode:packageExtension' + ]) { + group 'Delivery' + description 'Builds and tests all modules required by the Saros VS Code Extension' + + from project(':saros.lsp').jar + from 'vscode/vsix' + into 'build/distribution/vscode' +} + +task buildVSCode(dependsOn: [ + 'sarosLsp', + 'saros.vscode:buildExtension' +]) { + group 'VS Code' + description 'Builds the Saros VS Code plugin' +} + +task runVSCode(dependsOn: [ + 'sarosLsp', + 'saros.vscode:runExtension' +]) { + group 'VS Code' + description 'Builds and runs the Saros VS Code plugin' +} + task sarosIntellij(type: Copy, dependsOn: [ ':saros.picocontainer:test', ':saros.core:test', diff --git a/docs/contribute/development-environment.md b/docs/contribute/development-environment.md index f25113976b..bbf63fae72 100644 --- a/docs/contribute/development-environment.md +++ b/docs/contribute/development-environment.md @@ -86,6 +86,45 @@ This is necessary in order to allow IntelliJ to execute all build and test actio * Click on `Open` * Select the repository root as project root and click `OK` +## Develop with VS Code + +When developing with VS Code you rely on your installed extensions. When opening a file with an extension that has currently no support VS Code will ask for installing necessary plugins. When being asked either install what is suggested or install by your own preference. + +### Developing the VS Code Extension + +When developing the extension you have two ways to start the debugger: + +#### Using a launch configuration (preferred) + +Create the `launch.json` in your `.vscode` directory of the folder you opened with vscode. When using a `workspace` you have to reference your launch file there also. The `launch.json` has the following content: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "preLaunchTask": "gradle: prepareVSCode" + } + ] +} +``` + +#### Using gradle + +Using gradle tasks directly has the benefits of starting multiple instances of the `VS Code Extension Host` which is useful for testing multiple at once. Just open the available tasks (usually `CTRL+SHIFT+T`) and run `runVSCode` in order to start the extension in an `Extension Host`. When debugging is needed just hit `F5` and choose the NodeJs debugger and attach to the desired process. + +To make it more comfortable a keybinding can be assigned to that task (`keybindings.json`) or the launch configuration of the NodeJs debugger can be saved (`launch.json`) in order to attach via `F5`. + ## Develop Without an IDE If you prefer to develop with a text editor (like Vim or Emacs) you can build and test @@ -97,14 +136,17 @@ The following tasks are used to build and test the different Saros components: * `sarosEclipse` - Triggers the build and test of the Saros Eclipse Plugin * `sarosIntellij` - Triggers the build and test of the Saros IntelliJ Plugin * `sarosServer` - Triggers the build and test of the Saros Server +* `sarosLsp` - Triggers the build and test of the Saros Language Server +* `sarosVSCode` - Triggers the build and test of the Saros VS Code Extension * `prepareEclipse` - Executes all tasks which are required before developing in Eclipse * `runIde` - Starts a IntelliJ IDE containing the Saros Plugin. The IDE version depends on the value of `INTELLIJ_HOME` or the `intellijVersion` specified in the build file of the IntelliJ package. +* `runVSCode` - Starts an VS Code (Extension Host) with the Saros Extension. Use the node debugger to attach to the process for debugging. -In order to build the whole project without using existing build artifacts simply call `./gradlew cleanAll sarosEclipse sarosIntellij sarosServer`. +In order to build the whole project without using existing build artifacts simply call `./gradlew cleanAll sarosEclipse sarosIntellij sarosServer sarosVSCode`. Gradle checks whether the component specific sources are changed. Therefore a task become a NOP if nothing changed and the build results still exist. If you want to force Gradle to re-execute the tasks, you have to call `./gradlew --rerun-tasks ...` or call the `cleanAll` task before other tasks. -The final build results are copied into the directory `/build/distribute/(eclipse|intellij)`. +The final build results are copied into the directory `/build/distribute/(eclipse|intellij|vscode|lsp)`. ### Formatting via Standalone Google Java Formatter diff --git a/lsp/build.gradle b/lsp/build.gradle index f48f4aecc4..223c2d682a 100644 --- a/lsp/build.gradle +++ b/lsp/build.gradle @@ -4,7 +4,6 @@ plugins { dependencies { compile project(':saros.core') - compile project(':saros.server') compile 'org.apache.commons:commons-collections4:4.2' compile 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.8.1' compile 'org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.8.1' diff --git a/settings.gradle b/settings.gradle index 26bbd39055..5de937a1fe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,7 +3,7 @@ plugins { } String prefix = 'saros.' -List projectDirs = ['core', 'eclipse', 'intellij', 'server', 'lsp', 'stf', 'stf.test'].each { dir -> +List projectDirs = ['core', 'eclipse', 'vscode', 'intellij', 'server', 'lsp', 'stf', 'stf.test'].each { dir -> String projectName = prefix + dir include projectName project(":$projectName").projectDir = file(dir) diff --git a/vscode/.eslintrc.json b/vscode/.eslintrc.json new file mode 100644 index 0000000000..39887df00f --- /dev/null +++ b/vscode/.eslintrc.json @@ -0,0 +1,32 @@ +{ + "env": { + "es6": true, + "node": true, + "commonjs": true + }, + "extends": [ + "eslint:recommended", + "google" + ], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + }, + "project": "tsconfig.json" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars-experimental": "error" + }, + "root": true +} \ No newline at end of file diff --git a/vscode/.gitignore b/vscode/.gitignore new file mode 100644 index 0000000000..7330d84c5b --- /dev/null +++ b/vscode/.gitignore @@ -0,0 +1,32 @@ +# VS Code +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +out/* + +# VSCE +*.vsix + +# Logs +*.log +npm-debug.log* + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache \ No newline at end of file diff --git a/vscode/README.md b/vscode/README.md new file mode 100644 index 0000000000..458f49e972 --- /dev/null +++ b/vscode/README.md @@ -0,0 +1,25 @@ +# saros + +This is the Saros implementation for Visual Studio Code. + +## Features + +**TBD** + +## Requirements + +**TBD** + +## Extension Settings + +**TBD** + +## Known Issues + +**TBD** + +## Release Notes + +### 0.0.1 + +**TBD** \ No newline at end of file diff --git a/vscode/build.gradle b/vscode/build.gradle new file mode 100644 index 0000000000..d785b13d22 --- /dev/null +++ b/vscode/build.gradle @@ -0,0 +1,101 @@ +import org.apache.tools.ant.taskdefs.condition.Os +import groovy.json.JsonSlurper + +plugins { + id "com.github.node-gradle.node" version "2.2.0" +} + +apply plugin: 'com.github.node-gradle.node' + +def packageSlurper = new JsonSlurper() +def packageJson = packageSlurper.parse file('package.json') +version = packageJson.version + +def vscePath = './node_modules/vsce/out/vsce' + +node { + version = '10.14.1' + npmVersion = '6.4.1' + download = true +} + +npmInstall { + inputs.files fileTree(projectDir) +} + +task copyLsp(type: Copy) { + doFirst { + from("$rootDir/build/distribution/lsp") + into('dist') + } +} + +task buildExtension(dependsOn: [ + 'copyLsp', + 'npmInstall', + 'npm_run_webpack' + ]) { + group 'VS Code' + description 'Builds the extension' +} + +task runExtension(type: Exec, dependsOn: [ + 'buildExtension' +]) { + group 'VS Code' + description 'Builds and runs the extension' + + def execArgs = "code --extensionDevelopmentPath=${projectDir.absolutePath}" + + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + executable = 'cmd' + args = ["/c ${execArgs}"] + } else { + executable = 'sh' + args = [execArgs] + } + + workingDir = file('./dist').absolutePath +} + +task packageExtension(type: NodeTask, dependsOn: [ + 'copyLsp' +]) { + group 'VS Code' + description 'Packages the extension' + + doFirst { + delete 'vsix/*' + file('./vsix').mkdirs() + } + + ext.archiveName = "$project.name-${project.version}.vsix" + ext.destPath = "./vsix" + + script = file(vscePath) + args = ['package', '--out', destPath] +} + +task publishExtension(type: NodeTask, dependsOn: [ + 'copyLsp' +]) { + group 'VS Code' + description 'Publishes the extension' + + script = file(vscePath) + args = ['publish', 'patch'] + execOverrides { + workingDir = file('./') + } +} + +task unpublishExtension(type: NodeTask) { + group 'VS Code' + description 'Unpublishes the extension' + + script = file(vscePath) + args = ['unpublish', "${packageJson.publisher}.${packageJson.name}"] + execOverrides { + workingDir = file('./') + } +} \ No newline at end of file diff --git a/vscode/package.json b/vscode/package.json new file mode 100644 index 0000000000..65e4cb6646 --- /dev/null +++ b/vscode/package.json @@ -0,0 +1,76 @@ +{ + "name": "saros", + "displayName": "saros", + "description": "Saros implementation for VS Code.", + "version": "0.0.1", + "publisher": "mschaefer", + "repository": { + "url": "https://github.com/saros-project/saros" + }, + "engines": { + "vscode": "^1.38.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "*" + ], + "main": "./dist/extension", + "contributes": { + "commands": [ + { + "command": "saros.account.add", + "title": "Add Account", + "category": "Saros" + } + ], + "configuration": { + "type": "object", + "title": "Example configuration", + "properties": { + "sarosServer.trace.server": { + "scope": "window", + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "verbose", + "description": "Traces the communication between VS Code and the Saros server." + } + } + } + }, + "scripts": { + "vscode:prepublish": "webpack --mode production", + "webpack": "webpack --mode development", + "webpack-dev": "webpack --mode development --watch", + "test-compile": "tsc -p ./", + "pretest": "npm run compile", + "test": "node ./out/test/runTest.js", + "lint": "eslint */**/*.ts --quiet" + }, + "devDependencies": { + "@types/glob": "^7.1.1", + "@types/mocha": "^5.2.6", + "@types/node": "^10.12.21", + "@types/vscode": "^1.38.0", + "@typescript-eslint/eslint-plugin": "^2.19.2", + "@typescript-eslint/parser": "^2.19.2", + "eslint": "^6.8.0", + "eslint-config-google": "^0.14.0", + "glob": "^7.1.4", + "mocha": "^6.1.4", + "ts-loader": "^6.2.1", + "typescript": "^3.3.1", + "vsce": "^1.71.0", + "vscode-test": "^1.2.0", + "webpack": "^4.41.6", + "webpack-cli": "^3.3.10" + }, + "dependencies": { + "vscode-languageclient": "^5.2.1" + } +} diff --git a/vscode/src/account/activator.ts b/vscode/src/account/activator.ts new file mode 100644 index 0000000000..26c98226a5 --- /dev/null +++ b/vscode/src/account/activator.ts @@ -0,0 +1,20 @@ +import {commands} from 'vscode'; +import {SarosExtension} from '../core'; + +/** + * Activation function of the account module. + * + * @export + * @param {SarosExtension} extension - The instance of the extension + */ +export function activateAccounts(extension: SarosExtension) { + commands.registerCommand('saros.account.add', () => { + extension.onReady() + .then(() => { + return extension.client.addAccount(); + }) + .then((r) => { + console.log('Response was: ' + r.response); + }); + }); +} diff --git a/vscode/src/account/index.ts b/vscode/src/account/index.ts new file mode 100644 index 0000000000..147d32672f --- /dev/null +++ b/vscode/src/account/index.ts @@ -0,0 +1 @@ +export * from './activator'; diff --git a/vscode/src/core/index.ts b/vscode/src/core/index.ts new file mode 100644 index 0000000000..2596f1a132 --- /dev/null +++ b/vscode/src/core/index.ts @@ -0,0 +1,3 @@ +export * from './saros-lang-client'; +export * from './saros-lang-server'; +export * from './saros-extension'; diff --git a/vscode/src/core/saros-extension.ts b/vscode/src/core/saros-extension.ts new file mode 100644 index 0000000000..2c3b4e9bb6 --- /dev/null +++ b/vscode/src/core/saros-extension.ts @@ -0,0 +1,103 @@ +import {workspace, window, ExtensionContext} from 'vscode'; +import {SarosLangServer} from './saros-lang-server'; +import {SarosLangClient} from './saros-lang-client'; +import {LanguageClientOptions, + RevealOutputChannelOn} from 'vscode-languageclient'; + +/** + * The Saros extension. + * + * @export + * @class SarosExtension + */ +export class SarosExtension { + private context!: ExtensionContext; + public client!: SarosLangClient; + + /** + * Creates an instance of SarosExtension. + * + * @memberof SarosExtension + */ + constructor() { + + } + + /** + * Sets the context the extension runs on. + * + * @param {ExtensionContext} context - The extension context + * @return {SarosExtension} Itself + * @memberof SarosExtension + */ + setContext(context: ExtensionContext): SarosExtension { + this.context = context; + + return this; + } + + /** + * Initializes the extension. + * + * @memberof SarosExtension + */ + async init() { + if (!this.context) { + return Promise.reject(new Error('Context not set')); + } + + try { + const self = this; + + return new Promise((resolve) => { + const server = new SarosLangServer(self.context); + self.client = new SarosLangClient('sarosServer', 'Saros Server', + server.getStartFunc(), this.createClientOptions()); + this.context.subscriptions.push(self.client.start()); + + resolve(); + }); + } catch (ex) { + const msg = 'Error while activating plugin. ' + + (ex.message ? ex.message : ex); + return Promise.reject(new Error(msg)); + } + } + + /** + * Callback when extension is ready. + * + * @memberof SarosExtension + */ + async onReady() { + if (!this.client) { + console.log('onReady.reject'); + return Promise.reject(new Error('SarosExtension is not initialized')); + } + + console.log('onReady'); + return this.client.onReady(); + } + + /** + * Creates the client options. + * + * @private + * @return {LanguageClientOptions} The client options + * @memberof SarosExtension + */ + private createClientOptions(): LanguageClientOptions { + const clientOptions: LanguageClientOptions = { + documentSelector: ['plaintext'], + synchronize: { + fileEvents: workspace.createFileSystemWatcher('**/.clientrc'), + }, + outputChannel: window.createOutputChannel('Saros'), + revealOutputChannelOn: RevealOutputChannelOn.Info, + }; + + return clientOptions; + } +} + +export const sarosExtensionInstance = new SarosExtension(); diff --git a/vscode/src/core/saros-lang-client.ts b/vscode/src/core/saros-lang-client.ts new file mode 100644 index 0000000000..1e89c967fe --- /dev/null +++ b/vscode/src/core/saros-lang-client.ts @@ -0,0 +1,45 @@ +import {LanguageClient} from 'vscode-languageclient'; + +/** + * Response for adding new accounts. + * + * @export + * @interface AddAccountResponse + */ +export interface AddAccountResponse { + response: boolean; +} + +/** + * Request for adding new accounts. + * + * @export + * @interface AddAccountRequest + */ +export interface AddAccountRequest { + +} + +/** + * Custom language client for Saros protocol. + * + * @export + * @class SarosClient + * @extends {LanguageClient} + */ +export class SarosLangClient extends LanguageClient { + /** + * Adds a new account. + * + * @param {string} name - Account identifier + * @return {Thenable} The result + * @memberof SarosClient + */ + addAccount(): Thenable { + const request: AddAccountRequest = { + + }; + + return this.sendRequest('saros/account/add', request); + } +} diff --git a/vscode/src/core/saros-lang-server.ts b/vscode/src/core/saros-lang-server.ts new file mode 100644 index 0000000000..e35b941983 --- /dev/null +++ b/vscode/src/core/saros-lang-server.ts @@ -0,0 +1,176 @@ +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as process from 'child_process'; +import * as net from 'net'; +import {StreamInfo} from 'vscode-languageclient'; + +/** + * Encapsulation of the Saros Language server. + * + * @export + * @class SarosServer + */ +export class SarosLangServer { + /** + * Started process of the server. + * + * @private + * @type {process.ChildProcess} + * @memberof SarosServer + */ + private process?: process.ChildProcess; + + /** + * Creates an instance of SarosServer. + * + * @param {vscode.ExtensionContext} context - The extension context + * @memberof SarosServer + */ + constructor(private context: vscode.ExtensionContext) { + + } + + /** + * Starts the server process. + * + * @param {number} port - The port the server listens on for connection + * @memberof SarosServer + */ + public start(port: number): void { + if (this.process !== undefined) { + throw new Error('Server process is still running'); + } + + this.startProcess(port) + .withDebug(true) + .withExitAware(); + } + + /** + * Provides access to the start function. + * + * @remarks A free port will be determined and used. + * @return {function():Thenable} Function that starts the + * server and retuns the stream information + * @memberof SarosServer + */ + public getStartFunc(): () => Thenable { + return this.startServer; + } + + /** + * Starts the server on a random port. + * + * @return {Thenable} Stream information + * the server listens on + * @memberof SarosServer + */ + private startServer(): Thenable { + const self = this; + return new Promise((resolve) => { + const server = net.createServer((socket) => { + console.log('Creating server'); + + resolve({ + reader: socket, + writer: socket, + }); + + socket.on('end', () => console.log('Disconnected')); + }).on('error', (err) => { + // handle errors here + throw err; + }); + + // grab a random port. + server.listen(() => { + const port = (server.address() as net.AddressInfo).port; + + self.start(port); + }); + }); + } + + /** + * Starts the Saros server jar as process. + * + * @private + * @param {...any[]} args - Additional command line arguments for the server + * @return {SarosServer} Itself + * @memberof SarosServer + */ + private startProcess(...args: any[]): SarosLangServer { + const pathToJar = path.resolve(this.context.extensionPath, + 'dist', 'saros.lsp.jar'); + + console.log('spawning jar process'); + this.process = process.spawn('java', ['-jar', pathToJar].concat(args)); + + return this; + } + + /** + * Attaches listeners for debug informations and prints + * retrieved data to a newly created + * [output channel](#vscode.OutputChannel). + * + * @private + * @param {boolean} isEnabled - Wether debug output is redirected or not + * @return {SarosServer} Itself + * @memberof SarosServer + */ + private withDebug(isEnabled: boolean): SarosLangServer { + if (this.process === undefined) { + throw new Error('Server process is undefined'); + } + + if (!isEnabled) { + return this; + } + + const output = vscode.window.createOutputChannel('Saros (Debug)'); + + this.process.stdout.on('data', (data) => { + output.appendLine(data); + }); + + this.process.stderr.on('data', (data) => { + output.appendLine(data); + }); + + return this; + } + + /** + * Attaches listeners to observe termination of the server. + * + * @private + * @return {SarosServer} Itself + * @memberof SarosServer + */ + private withExitAware(): SarosLangServer { + if (this.process === undefined) { + throw new Error('Server process is undefined'); + } + + this.process.on('error', (error) => { + vscode.window.showErrorMessage( + `child process creating error with error ${error}`); + }); + + const self = this; + this.process.on('close', (code) => { + let showMessageFunc; + if (code === 0) { + showMessageFunc = vscode.window.showInformationMessage; + } else { + showMessageFunc = vscode.window.showWarningMessage; + } + + self.process = undefined; + showMessageFunc(`child process exited with code ${code}`); + }); + + return this; + } +} diff --git a/vscode/src/core/types.ts b/vscode/src/core/types.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts new file mode 100644 index 0000000000..6ad916acc6 --- /dev/null +++ b/vscode/src/extension.ts @@ -0,0 +1,51 @@ +import * as vscode from 'vscode'; +import {sarosExtensionInstance} from './core'; +import {activateAccounts} from './account'; + +/** + * Activation function of the extension. + * + * @export + * @param {vscode.ExtensionContext} context - The extension context + */ +export function activate(context: vscode.ExtensionContext) { + sarosExtensionInstance.setContext(context) + .init() + .then(() => { + activateAccounts(sarosExtensionInstance); + + console.log('Extension "Saros" is now active!'); + }) + .catch((reason) => { + console.log(reason); + vscode.window.showErrorMessage( + 'Saros extension did not start propertly.' + + 'Reason: ' + reason); + }); + + context.subscriptions.push(createStatusBar()); +} + +/** + * Creates the status bar. + * + * @return {Disposable} The status bar item as [disposable](#Disposable) + */ +function createStatusBar(): vscode.Disposable { + const statusBarItem = + vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, + Number.MAX_VALUE); + statusBarItem.text = 'Saros'; + statusBarItem.show(); + + return statusBarItem; +} + +/** + * Deactivation function of the extension. + * + * @export + */ +export function deactivate() { + console.log('deactivated'); +} diff --git a/vscode/tsconfig.json b/vscode/tsconfig.json new file mode 100644 index 0000000000..b65c745109 --- /dev/null +++ b/vscode/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "lib": [ + "es6" + ], + "sourceMap": true, + "rootDir": "src", + "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. */ + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} diff --git a/vscode/webpack.config.js b/vscode/webpack.config.js new file mode 100644 index 0000000000..d62c3663e1 --- /dev/null +++ b/vscode/webpack.config.js @@ -0,0 +1,52 @@ +// @ts-check + +'use strict'; + +const path = require('path'); + +/** + * @type {import('webpack').Configuration} + */ +const config = { + // vscode extensions run in a Node.js-context + // 📖 -> https://webpack.js.org/configuration/node/ + target: 'node', + + // the entry point of this extension, + // 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: './src/extension.ts', + output: { + // the bundle is stored in the 'dist' folder (check package.json), + // 📖 -> https://webpack.js.org/configuration/output/ + path: path.resolve(__dirname, 'dist'), + filename: 'extension.js', + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../[resource-path]', + }, + devtool: 'source-map', + externals: { + // the vscode-module is created on-the-fly and must be excluded. + // Add other modules that cannot be webpack'ed, + // 📖 -> https://webpack.js.org/configuration/externals/ + vscode: 'commonjs vscode', + }, + resolve: { + // support reading TypeScript and JavaScript files, + // 📖 -> https://github.com/TypeStrong/ts-loader + extensions: ['.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + loader: 'ts-loader', + }, + ], + }, + ], + }, +}; +module.exports = config;