diff --git a/src/app/index.ts b/src/app/index.ts index 3ebbbf8..6bd95bc 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -29,7 +29,8 @@ enum ExtensionType { TreeEditor = 'tree-editor', Empty = 'empty', Backend = 'backend', - DiagramEditor = 'diagram-editor' + DiagramEditor = 'diagram-editor', + VsCodeHelloWorld = 'vs-code-hello-world' } enum TemplateType { @@ -53,6 +54,7 @@ module.exports = class TheiaExtension extends Base { browser: boolean electron: boolean vscode: boolean + plugins: boolean theiaVersion: string lernaVersion: string skipInstall: boolean @@ -95,6 +97,12 @@ module.exports = class TheiaExtension extends Base { type: Boolean, default: true }) + this.option('plugins', { + alias: 'p', + description: 'Generate plugins folder and extend configs', + type: Boolean, + default: false + }) this.option('author', { alias: 'a', @@ -164,6 +172,7 @@ module.exports = class TheiaExtension extends Base { message: "The extension's type", choices: [ { value: ExtensionType.HelloWorld, name: 'Hello World' }, + { value: ExtensionType.VsCodeHelloWorld, name: 'Hello World (VS Code extension)' }, { value: ExtensionType.Widget, name: 'Widget (with unit tests)' }, { value: ExtensionType.LabelProvider, name: 'LabelProvider' }, { value: ExtensionType.TreeEditor, name: 'TreeEditor' }, @@ -188,10 +197,10 @@ module.exports = class TheiaExtension extends Base { (this.options as any).templateType = template; - if(template === TemplateType.Java) { + if (template === TemplateType.Java) { this.log('\x1b[32m%s\x1b[0m', 'The template will use an EMF source model on the server and generate a Theia extension ✓') } - if(template === TemplateType.Node) { + if (template === TemplateType.Node) { this.log('\x1b[32m%s\x1b[0m', 'The template will use a JSON based source model, node as a server and generate a Theia extension ✓') } } @@ -216,7 +225,7 @@ module.exports = class TheiaExtension extends Base { let unscopedExtensionName = '' let extensionPath = '' let extensionPrefix = '' - if(extensionName) { + if (extensionName) { unscopedExtensionName = extensionName[0] === '@' ? extensionName.substring(extensionName.indexOf('/') + 1) : extensionName; @@ -238,8 +247,10 @@ module.exports = class TheiaExtension extends Base { githubURL, theiaVersion: options["theia-version"], lernaVersion: options["lerna-version"], - backend: options["extensionType"] === ExtensionType.Backend - } + backend: options["extensionType"] === ExtensionType.Backend, + includeExtension: options["extensionType"] !== ExtensionType.VsCodeHelloWorld, + includePlugins: options.plugins || options["extensionType"] === ExtensionType.VsCodeHelloWorld, + }; this.params.dependencies = ''; this.params.browserDevDependencies = ''; if (this.params.extensionType === ExtensionType.TreeEditor) { @@ -249,7 +260,7 @@ module.exports = class TheiaExtension extends Base { if (this.params.extensionType === ExtensionType.Widget) { this.params.devdependencies = `,\n "@testing-library/react": "^11.2.7",\n "@types/jest": "^26.0.20",\n "jest": "^26.6.3",\n "ts-node": "^10.9.1",\n "ts-jest": "^26.5.6"`; this.params.scripts = `,\n "test": "jest --config configs/jest.config.ts"`; - this.params.rootscripts =`,\n "test": "cd ${this.params.extensionPath} && yarn test"`; + this.params.rootscripts = `,\n "test": "cd ${this.params.extensionPath} && yarn test"`; this.params.containsTests = true; } options.params = this.params @@ -298,12 +309,17 @@ module.exports = class TheiaExtension extends Base { { params: this.params } ); } + if (this.params.plugins || this.params.extensionType === ExtensionType.VsCodeHelloWorld) { + this.fs.write('plugins/.gitkeep', 'Folder of VS Code extensions (*.vsix) to be installed by default.'); + } + } + if (this.params.extensionType !== ExtensionType.VsCodeHelloWorld) { + this.fs.copyTpl( + this.templatePath('extension-package.json'), + this.extensionPath('package.json'), + { params: this.params } + ); } - this.fs.copyTpl( - this.templatePath('extension-package.json'), - this.extensionPath('package.json'), - { params: this.params } - ); this.fs.copyTpl( this.templatePath('tsconfig.json'), this.extensionPath('tsconfig.json'), @@ -330,6 +346,25 @@ module.exports = class TheiaExtension extends Base { ); } + /** vs-code-hello-world */ + if (this.params.extensionType === ExtensionType.VsCodeHelloWorld) { + this.fs.copyTpl( + this.templatePath('vscode-hello-world/package.json'), + this.extensionPath('package.json'), + { params: this.params } + ); + this.fs.copyTpl( + this.templatePath('vscode-hello-world/gitignore'), + this.extensionPath('.gitignore'), + { params: this.params } + ); + this.fs.copyTpl( + this.templatePath('vscode-hello-world/extension.ts'), + this.extensionPath(`src/extension.ts`), + { params: this.params } + ); + } + /** empty */ if (this.params.extensionType === ExtensionType.Empty) { this.fs.copyTpl( @@ -474,7 +509,7 @@ module.exports = class TheiaExtension extends Base { if (this.params.extensionType === ExtensionType.DiagramEditor) { const baseDir = `./glsp-examples-${glspExamplesRepositoryTag}`; let templatePath = ''; - if(this.params.templateType == TemplateType.Java) { + if (this.params.templateType == TemplateType.Java) { templatePath = '/project-templates/java-emf-theia'; } else if (this.params.templateType == TemplateType.Node) { templatePath = '/project-templates/node-json-theia'; @@ -483,9 +518,9 @@ module.exports = class TheiaExtension extends Base { } const done = this.async(); - request.get(`https://github.com/eclipse-glsp/glsp-examples/archive/refs/tags/${glspExamplesRepositoryTag}.tar.gz`).pipe(tar.x().on('close',() => { - fs.copy(baseDir+'/README.md', './README.md'); - fs.copy(baseDir+templatePath, './').then(() => { + request.get(`https://github.com/eclipse-glsp/glsp-examples/archive/refs/tags/${glspExamplesRepositoryTag}.tar.gz`).pipe(tar.x().on('close', () => { + fs.copy(baseDir + '/README.md', './README.md'); + fs.copy(baseDir + templatePath, './').then(() => { fs.rm(baseDir, { recursive: true }); done(); }); @@ -504,22 +539,22 @@ module.exports = class TheiaExtension extends Base { const command = this.spawnCommand('yarn', []); - command.on('close', (code:number) => { - if (code === 0 ) { + command.on('close', (code: number) => { + if (code === 0) { this.log( '\x1b[32m%s\x1b[0m', '\nThe DiagramEditor Example has been generated and all dependencies installed\n\nCheck the Readme to get started.' ); } else { - this.log('\x1b[31m%s\x1b[0m','Command "yarn" failed. Please see above for the reported error message.'); + this.log('\x1b[31m%s\x1b[0m', 'Command "yarn" failed. Please see above for the reported error message.'); process.exit(code); } }); } else { const command = this.spawnCommand('yarn', []); - - command.on('close', function(code: number){ - if (code !== 0 ) { + + command.on('close', function (code: number) { + if (code !== 0) { process.exit(code); } }) diff --git a/templates/app-browser-package.json b/templates/app-browser-package.json index 5a6d3f1..14fd695 100644 --- a/templates/app-browser-package.json +++ b/templates/app-browser-package.json @@ -13,15 +13,18 @@ "@theia/preferences": "<%= params.theiaVersion %>", "@theia/process": "<%= params.theiaVersion %>", "@theia/terminal": "<%= params.theiaVersion %>", - "@theia/workspace": "<%= params.theiaVersion %>", - "<%= params.extensionName %>": "<%= params.version %>" + "@theia/workspace": "<%= params.theiaVersion %>"<% if (params.includeExtension) { %>, + "<%= params.extensionName %>": "<%= params.version %>"<% } %><% if (params.includePlugins) { %>, + "@theia/plugin": "<%= params.theiaVersion %>", + "@theia/plugin-ext": "<%= params.theiaVersion %>", + "@theia/plugin-ext-vscode": "<%= params.theiaVersion %>"<% } %> }, "devDependencies": { "@theia/cli": "<%= params.theiaVersion %>"<% if (params.browserDevDependencies) { %><%- params.browserDevDependencies %><% } %> }, "scripts": { "prepare": "theia build --mode development", - "start": "theia start", + "start": "theia start<% if (params.includePlugins) { %> --plugins=local-dir:../plugins<% } %>", "watch": "theia build --watch --mode development" }, "theia": { diff --git a/templates/app-electron-package.json b/templates/app-electron-package.json index 90e0bff..ef00d87 100644 --- a/templates/app-electron-package.json +++ b/templates/app-electron-package.json @@ -14,8 +14,11 @@ "@theia/preferences": "<%= params.theiaVersion %>", "@theia/process": "<%= params.theiaVersion %>", "@theia/terminal": "<%= params.theiaVersion %>", - "@theia/workspace": "<%= params.theiaVersion %>", - "<%= params.extensionName %>": "<%= params.version %>" + "@theia/workspace": "<%= params.theiaVersion %>"<% if (params.includeExtension) { %>, + "<%= params.extensionName %>": "<%= params.version %>"<% } %><% if (params.includePlugins) { %>, + "@theia/plugin": "<%= params.theiaVersion %>", + "@theia/plugin-ext": "<%= params.theiaVersion %>", + "@theia/plugin-ext-vscode": "<%= params.theiaVersion %>"<% } %> }, "devDependencies": { "@theia/cli": "<%= params.theiaVersion %>", @@ -23,7 +26,7 @@ }, "scripts": { "prepare": "theia build --mode development", - "start": "theia start", + "start": "theia start<% if (params.includePlugins) { %> --plugins=local-dir:../plugins<% } %>", "watch": "theia build --watch --mode development" }, "theia": { diff --git a/templates/launch.json b/templates/launch.json index 58b45a2..d6645c5 100644 --- a/templates/launch.json +++ b/templates/launch.json @@ -12,10 +12,12 @@ "args": [ "--loglevel=debug", "--port=3000", - "--no-cluster" + "--no-cluster"<% if (params.includePlugins) { %>, + "--plugins=local-dir:${workspaceRoot}/plugins"<% } %> ], "env": { - "NODE_ENV": "development" + "NODE_ENV": "development", + "NODE_OPTIONS": "--enable-source-maps" }, "sourceMaps": true, "outFiles": [ @@ -40,10 +42,12 @@ "args": [ "--loglevel=debug", "--hostname=localhost", - "--no-cluster" + "--no-cluster"<% if (params.includePlugins) { %>, + "--plugins=local-dir:${workspaceRoot}/plugins"<% } %> ], "env": { - "NODE_ENV": "development" + "NODE_ENV": "development", + "NODE_OPTIONS": "--enable-source-maps" }, "sourceMaps": true, "outFiles": [ diff --git a/templates/root-package.json b/templates/root-package.json index 55e3961..df4a79a 100644 --- a/templates/root-package.json +++ b/templates/root-package.json @@ -13,5 +13,6 @@ }, "workspaces": [ "<%= params.extensionPath %>"<% if (params.browser) { %>, "browser-app"<% } %><% if (params.electron) { %>, "electron-app"<% } %> - ] + ]<% if (params.includePlugins) { %>, + "theiaPluginsDir": "plugins"<% } %> } diff --git a/templates/vscode-hello-world/extension.ts b/templates/vscode-hello-world/extension.ts new file mode 100644 index 0000000..3419151 --- /dev/null +++ b/templates/vscode-hello-world/extension.ts @@ -0,0 +1,41 @@ +// The following code is based on +// https://github.com/microsoft/vscode-extension-samples/blob/7b9a0a8c4c631e393862c3e767c7be26bb2233b2/.base-sample/src/extension.ts +// which was published under MIT License (https://github.com/microsoft/vscode-extension-samples/blob/main/LICENSE) +// For more information see https://code.visualstudio.com/api + +// 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 + +/** + * @param {vscode.ExtensionContext} context + */ +function activate(context: vscode.ExtensionContext) { + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "<%= params.extensionName %>" is now active!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + let disposable = vscode.commands.registerCommand('<%= params.extensionName %>.helloWorld', () => { + // The code you place here will be executed every time your command is executed + + // Display a message box to the user + vscode.window.showInformationMessage('Hello World from a VS Code extension!'); + }); + + context.subscriptions.push(disposable); +} + +// this method is called when your extension is deactivated +function deactivate() {} + +// eslint-disable-next-line no-undef +module.exports = { + activate, + deactivate +} \ No newline at end of file diff --git a/templates/vscode-hello-world/gitignore b/templates/vscode-hello-world/gitignore new file mode 100644 index 0000000..dfacd4d --- /dev/null +++ b/templates/vscode-hello-world/gitignore @@ -0,0 +1 @@ +*.vsix \ No newline at end of file diff --git a/templates/vscode-hello-world/package.json b/templates/vscode-hello-world/package.json new file mode 100644 index 0000000..567db4b --- /dev/null +++ b/templates/vscode-hello-world/package.json @@ -0,0 +1,55 @@ +{ + "name": "<%= params.extensionName %>", + "displayName": "Hello World (VS Code extension)", + "engines": { + "vscode": "^1.32.0" + }, + "version": "<%= params.version %>",<% + if (params.description) { %> + "description": "<%= params.description %>", + <%} %><% + if (params.author) { %> + "author": "<%= params.author %>", + <% } %><% + if (params.license) { %> + "license": "<%= params.license %>", + <% } %><% + if (params.githubURL) { %> + "repository": { + "type": "git", + "url": "<%= params.githubURL %>.git" + }, + "bugs": { + "url": "<%= params.githubURL %>/issues" + }, + "homepage": "<%= params.githubURL %>",<% } %> + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:<%= params.extensionName %>.helloWorld" + ], + "main": "./lib/extension.js", + "contributes": { + "commands": [ + { + "command": "<%= params.extensionName %>.helloWorld", + "title": "Hello World (VS Code extension)" + } + ] + }, + "scripts": { + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "prepare": "yarn run clean && yarn run build && yarn symlink", + "clean": "rimraf lib", + "build": "yarn run compile", + "symlink": "symlink-dir . ../plugins/<%= params.extensionName %>" + }, + "devDependencies": { + "@types/vscode": "^1.32.0", + "symlink-dir": "latest", + "rimraf": "latest", + "typescript": "latest" + } +} \ No newline at end of file