diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17939bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +node_modules/ +theia-plugin/npm-debug.log + +tooling/out/ +package-lock.json \ No newline at end of file diff --git a/tooling/.babelrc b/tooling/.babelrc new file mode 100644 index 0000000..e4cc406 --- /dev/null +++ b/tooling/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + ["@babel/preset-env", { "modules": false }], + "@babel/preset-typescript" + ] +} \ No newline at end of file diff --git a/tooling/package.json b/tooling/package.json new file mode 100644 index 0000000..ca92dae --- /dev/null +++ b/tooling/package.json @@ -0,0 +1,30 @@ +{ + "name": "jsonforms-tooling", + "version": "0.0.1", + "description": "", + "author": "EclipseSource", + "main": "out/index", + "typings": "out/index", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "babel src -d out --extensions \".ts,.tsx\"", + "compile": "rimraf out && tsc -p ./ --declaration", + "watch": "tsc -watch -p ./ --declaration" + }, + "dependencies": { + "@jsonforms/core": "^2.0.12-rc.3", + "ajv": "^6.5.5", + "npm": "^6.4.1", + "simple-git": "^1.107.0" + }, + "type-check": "tsc", + "devDependencies": { + "@babel/cli": "^7.1.5", + "@babel/core": "^7.1.6", + "@babel/preset-env": "^7.1.6", + "@babel/preset-typescript": "^7.1.0", + "@types/node": "^10.12.7", + "rimraf": "^2.6.2", + "typescript": "^3.1.6" + } +} diff --git a/tooling/src/index.ts b/tooling/src/index.ts new file mode 100644 index 0000000..1cf9064 --- /dev/null +++ b/tooling/src/index.ts @@ -0,0 +1,93 @@ +import * as simplegit from 'simple-git/promise'; +import * as jsonforms from '@jsonforms/core'; +import * as cp from 'child_process'; +import { writeFile, readFile } from 'fs'; +import * as Ajv from 'ajv'; +var npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +/* + * Clones a git repository and runs npm install on it + * @param {string} repo the name of the repo that should be cloned + * @param {string} path to the folder, where the repo should be cloned into + * @param {function} callback forwards the current status to the caller + */ +export function cloneAndInstall(repo: string, path: string, callback: (result: string, type?: string) => void, name?: string) { + var url = ''; + switch(repo) { + case 'example': + url = 'https://github.com/eclipsesource/make-it-happen-react'; + break; + case 'seed': + url = 'https://github.com/eclipsesource/jsonforms-react-seed'; + break; + } + const git = simplegit(); + callback('Starting to clone repo'); + git.clone(url, path) + .then(function() { + callback('Finished to clone repo'); + callback('Running npm install'); + const result = cp.spawnSync( npm, ['install'], { + cwd: path + }); + callback(result.signal); + }) + .catch((err: any) => {callback(err.message, 'err')}); +} + +/** + * Generates the default UI Schema from a json schema + * @param {string} path path to the json schema file + * @param {function} callback forwards the current status to the caller + */ +export function generateUISchema(path: string, name: string, callback: (result: string, type?: string) => void) { + // Read JSON Schema file + readFile(path, 'utf8', (err, data) => { + if (err) { + callback(err.message, 'err'); + return; + } + + var jsonSchema = JSON.parse(data); + validateJSONSchema(jsonSchema, function(err?: string) { + if (err) { + callback(err, 'err'); + return; + } + + var jsonUISchema = jsonforms.generateDefaultUISchema(jsonSchema); + + // Check if windows or linux filesystem + if(process.platform === 'win32') { + var newPath = path.substring(0, path.lastIndexOf("\\"))+'\\'+name; + } else { + var newPath = path.substring(0, path.lastIndexOf("/"))+'/'+name; + } + + // Write UI Schema file + writeFile(newPath, JSON.stringify(jsonUISchema,null, 2), (err) => { + if (err) { + callback(err.message, 'err'); + return; + } + + callback('Successfully generated UI schema'); + }); + }); + }); +} + +/** + * Validate a given JSON Schema + * @param {string} path path to the json schema file + * @param {function} callback forwards the current status to the caller + */ +function validateJSONSchema(schema: Object, callback: (err?: string) => void) { + var ajv = new Ajv(); + try { + ajv.compile(schema); + callback(); + } catch (error) { + callback(error.message); + } +} diff --git a/tooling/tsconfig.json b/tooling/tsconfig.json new file mode 100644 index 0000000..2933636 --- /dev/null +++ b/tooling/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "lib": [ + "es6" + ], + "noEmit": false, + "pretty": true, + "skipLibCheck": true, + "sourceMap": true, + "rootDir": "src", + /* Strict Type-Checking Option */ + "strict": true, /* enable all strict type-checking options */ + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */ + // "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" + ] +} \ No newline at end of file diff --git a/vscode-extension/package.json b/vscode-extension/package.json index f5b845c..b8473cd 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -1,6 +1,6 @@ { - "name": "vscode-extension", - "displayName": "vscode-extension", + "name": "jsoforms-tooling-vscode", + "displayName": "JSONForms Tooling", "description": "Extension for the VS Code editor to support JSONForms Tooling, making it easier for developers to start new project or try out the library.", "version": "0.0.1", "publisher": "EclipseSource", @@ -11,16 +11,45 @@ "Other" ], "activationEvents": [ - "onCommand:extension.sayHello" + "onCommand:extension.createExampleProject", + "onCommand:extension.createSeedProject", + "onCommand:extension.generateUISchema" ], "main": "./out/extension", "contributes": { "commands": [ { - "command": "extension.sayHello", - "title": "Hello World" + "command": "extension.createExampleProject", + "title": "Create Example Project" + }, + { + "command": "extension.createSeedProject", + "title": "Create Seed Project" + }, + { + "command": "extension.generateUISchema", + "title": "Generate UI Schema" } - ] + ], + "menus": { + "explorer/context": [ + { + "when": "explorerResourceIsFolder", + "command": "extension.createExampleProject", + "group": "jsonforms" + }, + { + "when": "explorerResourceIsFolder", + "command": "extension.createSeedProject", + "group": "jsonforms" + }, + { + "when": "resourceExtname == .json", + "command": "extension.generateUISchema", + "group": "jsonforms" + } + ] + } }, "scripts": { "vscode:prepublish": "npm run compile", @@ -30,9 +59,11 @@ "test": "npm run compile && node ./node_modules/vscode/bin/test" }, "devDependencies": { - "typescript": "^2.6.1", - "vscode": "^1.1.21", + "@types/mocha": "^2.2.42", "@types/node": "^8.10.25", - "@types/mocha": "^2.2.42" - } + "jsonforms-tooling": "file:../tooling", + "typescript": "^2.6.1", + "vscode": "^1.1.21" + }, + "dependencies": {} } diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts index d38a48c..f54172d 100644 --- a/vscode-extension/src/extension.ts +++ b/vscode-extension/src/extension.ts @@ -2,26 +2,89 @@ // 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'; +var tooling = require('jsonforms-tooling'); // 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) { - // 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 "vscode-extension" 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('extension.sayHello', () => { - // The code you place here will be executed every time your command is executed + let createExampleProject = vscode.commands.registerCommand('extension.createExampleProject', (args: any) => { + if(!args) { + showMessage('You can only run this on a folder', 'err'); + return; + } else { + let path = args.fsPath; + showMessage('Creating example project: '+path); + tooling.cloneAndInstall('example', path, function(result: string, type: string) { + showMessage(result, type); + }); + } + }); + + let createSeedProject = vscode.commands.registerCommand('extension.createSeedProject', (args: any) => { + if(!args) { + showMessage('You can only run this on a folder', 'err'); + return; + } else { + let options: vscode.InputBoxOptions = { + prompt: "Label: ", + placeHolder: "Enter a name for your seed project" + } + vscode.window.showInputBox(options).then(name => { + if (!name) name = 'jsonforms-seed'; + let path = args.fsPath; + showMessage('Creating seed project: '+path); + tooling.cloneAndInstall('seed', path, function(result: string, type: string) { + showMessage(result, type); + }, name); + }); + } + }); - // Display a message box to the user - vscode.window.showInformationMessage('Hello World!'); + let generateUISchema = vscode.commands.registerCommand('extension.generateUISchema', (args: any) => { + if(!args) { + showMessage('You can only run this on a json file', 'err'); + return; + } else { + let options: vscode.InputBoxOptions = { + prompt: "Label: ", + placeHolder: "Enter a filename for your UI Schema (default: ui-schema.json)" + } + vscode.window.showInputBox(options).then(name => { + if (!name) name = 'ui-schema.json'; + let path = args.fsPath; + showMessage('Generating UI Schema: '+path); + tooling.generateUISchema(path, name, function(result: string, type: string) { + showMessage(result, type); + }); + }); + } }); - context.subscriptions.push(disposable); + context.subscriptions.push(createExampleProject); + context.subscriptions.push(createSeedProject); + context.subscriptions.push(generateUISchema); +} + +/** + * Show Visual Studio Code Message + * @param {string} message the message that should be displayed + * @param {string} type the type of the message + */ +function showMessage(message: string, type?: string) { + switch(type) { + case 'err': + vscode.window.showErrorMessage(message); + break; + case 'war': + vscode.window.showWarningMessage(message); + break; + default: + vscode.window.showInformationMessage(message); + } } // this method is called when your extension is deactivated