├── .gitattributes ├── .travis.yml ├── .gitignore ├── docs ├── ui5-schemas.png └── xml-code-completion.gif ├── templates └── misc.xml ├── .eslintrc.js ├── .editorconfig ├── lib ├── Util.js ├── main.js ├── download.js ├── link.js ├── Options.js └── upgrade.js ├── LICENSE ├── package.json ├── bin └── index.js └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "8.10" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | node_modules 4 | .tmp 5 | .idea 6 | npm-debug.log 7 | *.iml 8 | -------------------------------------------------------------------------------- /docs/ui5-schemas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5experts/ui5-schemas/HEAD/docs/ui5-schemas.png -------------------------------------------------------------------------------- /templates/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/xml-code-completion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5experts/ui5-schemas/HEAD/docs/xml-code-completion.gif -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': 'airbnb-base', 3 | 'rules': { 4 | 'linebreak-style': 0 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /lib/Util.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | 4 | class Util { 5 | static getAppDataDir() { 6 | let appDataPath = process.env.APPDATA; 7 | if (!appDataPath) { 8 | if (process.platform === 'darwin') { 9 | appDataPath = path.join(os.homedir(), 'Library/Application Support'); 10 | } else if (process.platform === 'linux') { 11 | appDataPath = path.join(os.homedir(), '.local/share'); 12 | } 13 | } 14 | return appDataPath; 15 | } 16 | } 17 | 18 | module.exports = Util; 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2018 UI5 Experts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-schemas", 3 | "version": "0.5.0", 4 | "description": "UI5 Schemas allows you to develop SAPUI5/OpenUI5 XML at a maximum convenience. It downloads, upgrades and sets up SAPUI5/OpenUI5 XML schemas for a better development experience in your favorite IDE (if it is WebStorm ;).", 5 | "author": { 6 | "name": "Christian Schuff", 7 | "email": "christian.schuff@ui5experts.com", 8 | "url": "http://www.ui5experts.com" 9 | }, 10 | "bin": "bin/index.js", 11 | "scripts": { 12 | "release": "np", 13 | "test": "npm run -s eslint", 14 | "eslint": "eslint 'bin/**/*.js' 'lib/**/*.js'" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git@github.com:ui5experts/ui5-schemas.git" 19 | }, 20 | "keywords": [ 21 | "SAPUI5", 22 | "OpenUI5", 23 | "UI5", 24 | "SAP", 25 | "XML", 26 | "XMLView", 27 | "XML-Schema", 28 | "XSD", 29 | "WebStorm", 30 | "Eclipse", 31 | "Code Completion", 32 | "Development", 33 | "Intellisense" 34 | ], 35 | "license": "MIT", 36 | "engines": { 37 | "node": "^8.10.0" 38 | }, 39 | "dependencies": { 40 | "fs-extra": "8.1.0", 41 | "minilog": "3.1.0", 42 | "request": "2.88.0", 43 | "request-promise": "4.2.4", 44 | "xml2js": "0.4.22", 45 | "yargs": "14.0.0" 46 | }, 47 | "devDependencies": { 48 | "eslint": "5.16.0", 49 | "eslint-config-airbnb-base": "13.2.0", 50 | "eslint-plugin-import": "2.18.2", 51 | "np": "5.0.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | const Minilog = require('minilog'); 2 | 3 | const download = require('./download'); 4 | const upgrade = require('./upgrade'); 5 | const link = require('./link'); 6 | 7 | const log = Minilog('ui5-schemas'); 8 | 9 | function createLogger(debug) { 10 | const myFilter = new Minilog.Filter(); 11 | myFilter.clear().deny('ui5-schemas', debug ? '*' : 'debug'); 12 | Minilog.pipe(myFilter) 13 | .pipe(Minilog.backends.console.formatMinilog) 14 | .pipe(Minilog.backends.console); 15 | } 16 | 17 | 18 | // TODO: Return Promise 19 | const main = async function main(options) { 20 | createLogger(options.debug); 21 | 22 | await options.initialize(); 23 | 24 | log.info(`Preparing '${options.sdk}' schemas in version '${options.version || 'latest'}'...`); 25 | 26 | let allLibs = {}; 27 | 28 | // do the downloading and encapsulate in Promise 29 | const dlPromise = new Promise((resolve, reject) => { 30 | download.allLibsFile(options) 31 | .then((allLibsJSON) => { 32 | allLibs = allLibsJSON; 33 | download.schemas(options, allLibs) 34 | .then((values) => { 35 | resolve(values); 36 | }) 37 | .catch((e) => { 38 | reject(e); 39 | }); 40 | }) 41 | .catch((e) => { 42 | reject(e); 43 | }); 44 | }); 45 | 46 | 47 | dlPromise.then(() => { 48 | log.info('Yay, all schema files were successfully downloaded!'); 49 | if (options.upgrade) { 50 | upgrade(options, allLibs); 51 | } 52 | if (options.link) { 53 | link(options, allLibs); 54 | } 55 | }, (e) => { 56 | log.error('Damn... Something went all wrong while downloading the schema files!'); 57 | log.error(`Are you sure the version '${options.version}' is still available on '${options.sdk}' download pages?`); 58 | log.error(e); 59 | }); 60 | }; 61 | 62 | 63 | module.exports = main; 64 | -------------------------------------------------------------------------------- /lib/download.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const request = require('request'); 3 | const fs = require('fs-extra'); 4 | const Minilog = require('minilog'); 5 | 6 | const log = Minilog('ui5-schemas'); 7 | 8 | const schemaSuffix = '.xsd'; 9 | 10 | 11 | module.exports = { 12 | allLibsFile: function allLibsFile(options) { 13 | log.debug('[download]', `Downloading all libraries metafile from '${options.allLibsUrl}'`); 14 | return new Promise((resolve, reject) => { 15 | request(options.allLibsUrl, (err, res, body) => { 16 | if (err) { 17 | reject(err); 18 | return; 19 | } 20 | try { 21 | const allLibs = JSON.parse(body); 22 | resolve(allLibs); 23 | } catch (e) { 24 | reject(e); 25 | } 26 | }); 27 | }); 28 | }, 29 | 30 | schemas: function schemas(options, allLibs) { 31 | const allLibsRegistry = allLibs.all_libs; 32 | const downloadPromises = []; 33 | 34 | for (let i = 0; i < allLibsRegistry.length; i += 1) { 35 | const schemaFileName = allLibsRegistry[i].entry.replace(/\//g, '.') + schemaSuffix; 36 | const schemaFileURL = `${options.schemaBaseUrl}/${schemaFileName}`; 37 | 38 | downloadPromises.push(new Promise((resolve, reject) => { 39 | log.debug('[download]', `Downloading library schema from '${schemaFileURL}'`); 40 | request(schemaFileURL, (err, res, body) => { 41 | if (err) { 42 | reject(err); 43 | return; 44 | } 45 | 46 | const targetFile = path.join(options.schemaTargetBasePath, schemaFileName); 47 | allLibsRegistry[i].location = targetFile; 48 | log.debug('[download]', `Writing library schema file to '${targetFile}'`); 49 | fs.outputFile(targetFile, body) 50 | .then((data) => { 51 | resolve(data); 52 | }) 53 | .catch((fileErr) => { 54 | reject(fileErr); 55 | }); 56 | }); 57 | })); 58 | } 59 | 60 | return Promise.all(downloadPromises); 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const yargs = require('yargs'); 3 | 4 | const ui5Schemas = require('../lib/main'); 5 | const Options = require('../lib/Options'); 6 | 7 | 8 | const { argv } = yargs.usage('Usage: ui5-schemas [options]') 9 | .example('ui5-schemas --sdk openui5 --version 1.28.15', 'Setup with openui5 schemas in version 1.28.15') 10 | .example('ui5-schemas --sdk openui5nightly', 'Setup with openui5nightly') 11 | .example('ui5-schemas --origin \'/Users/cschuff/Downloads/sapui5-sdk-1.65.1\'', 'Setup schemas from local sdk download') 12 | .example('ui5-schemas --origin \'https://my.abap.system/sap/public/bc/ui5_ui5\'', 'Setup schemas from sdk on an ABAP system') 13 | .example('ui5-schemas --no-upgrade', 'Setup schemas without schema enhancement') 14 | .example('npx ui5-schemas --sdk openui5 --version 1.28.15', 'NPM5') 15 | 16 | .option('origin', { 17 | describe: 'The src url (sdk base url) or path (sdk root dir) to be used for schema loading.', 18 | type: 'string', 19 | }) 20 | 21 | .option('sdk', { 22 | demandOption: true, 23 | default: 'sapui5', 24 | describe: 'The sdk to be used.', 25 | choices: ['sapui5', 'openui5', 'openui5nightly'], 26 | type: 'string', 27 | }) 28 | 29 | .version(false) 30 | .option('version', { 31 | alias: 'v', 32 | demandOption: true, 33 | default: '', 34 | describe: 'The UI5 version to be used, defaults to \'\' which means latest.', 35 | type: 'string', 36 | }) 37 | 38 | // .describe('libs', 'Comma-separated list of UI5 libraries that should be downloaded (and linked' 39 | // + ' to your project), e.g. "sap.ui.core, sap.m" .') 40 | 41 | .option('upgrade', { 42 | demandOption: true, 43 | default: true, 44 | describe: 'Whether to upgrade UI5 schemas for a better development experience or leave them untouched.', 45 | type: 'boolean', 46 | }) 47 | 48 | .option('link', { 49 | demandOption: true, 50 | default: true, 51 | describe: 'Whether to auto-link UI5 schemas with your favorite IDE (if it is WebStorm ;).', 52 | type: 'boolean', 53 | }) 54 | 55 | .option('debug', { 56 | demandOption: true, 57 | default: process.env.SYSTEM_DEBUG === 'true', 58 | describe: 'Whether to show debug output', 59 | type: 'boolean', 60 | }) 61 | 62 | .help('h') 63 | .alias('h', 'help'); 64 | 65 | 66 | const options = new Options(argv); 67 | ui5Schemas(options); 68 | -------------------------------------------------------------------------------- /lib/link.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const xml2js = require('xml2js'); 4 | const Minilog = require('minilog'); 5 | 6 | const parser = new xml2js.Parser(); 7 | const builder = new xml2js.Builder(); 8 | const log = Minilog('ui5-schemas'); 9 | 10 | 11 | const idePaths = { 12 | idea: { 13 | base: path.join(process.cwd(), '.idea'), 14 | schemaRegistry: path.join(process.cwd(), '.idea/misc.xml'), 15 | template: path.join(__dirname, '../templates/misc.xml'), 16 | }, 17 | eclipse: '', 18 | }; 19 | 20 | 21 | function createIdeaRegEntry(url, location) { 22 | return { 23 | $: { 24 | url, 25 | location: `${location}`, 26 | }, 27 | }; 28 | } 29 | 30 | 31 | function registerLibs(data, allLibs) { 32 | try { 33 | parser.parseString(data, (err, result) => { 34 | if (err) { 35 | log.error('[link]', err); 36 | return; 37 | } 38 | 39 | const projectResourcesElement = { 40 | $: { 41 | name: 'ProjectResources', 42 | }, 43 | resource: [], 44 | }; 45 | 46 | // ensure target element exists 47 | const misc = result; 48 | misc.project = misc.project || {}; 49 | misc.project.component = misc.project.component || [projectResourcesElement]; 50 | 51 | let projectResourcesObj = misc.project.component.find(element => element.$.name === 'ProjectResources'); 52 | if (!projectResourcesObj) { 53 | projectResourcesObj = projectResourcesElement; 54 | misc.project.component.push(projectResourcesObj); 55 | } 56 | 57 | const projectResources = projectResourcesObj.resource; 58 | const allLibsRegistry = allLibs.all_libs; 59 | for (let i = 0; i < allLibsRegistry.length; i += 1) { 60 | const lib = allLibsRegistry[i]; 61 | const schemaURL = lib.entry.replace(/\//g, '.'); 62 | 63 | let libRegEntry = projectResources.filter(item => item.$.url === schemaURL); 64 | if (libRegEntry.length) { 65 | log.debug('[link]', `Updating schema location of lib '${schemaURL}' ...`); 66 | libRegEntry[0].$.location = lib.location; 67 | } else { 68 | log.debug('[link]', `Creating new schema entry for lib '${schemaURL}' ...`); 69 | libRegEntry = createIdeaRegEntry(schemaURL, lib.location); 70 | projectResources.push(libRegEntry); 71 | } 72 | } 73 | 74 | fs.outputFile(idePaths.idea.schemaRegistry, builder.buildObject(misc)) 75 | .then(() => { 76 | log.info('[link]', 'Schemas successfully registered!'); 77 | }) 78 | .catch(() => { 79 | log.error('[link]', 'Ough... Problem occured during schema registration!'); 80 | }); 81 | }); 82 | } catch (e) { 83 | log.error(e); 84 | } 85 | } 86 | 87 | 88 | function idea(options, allLibs) { 89 | fs.pathExists(idePaths.idea.base) 90 | .then((exists) => { 91 | if (!exists) { 92 | return; 93 | } 94 | log.info('[link]', 'Discovered an .idea project! Registering schemas...'); 95 | 96 | fs.readFile(idePaths.idea.schemaRegistry) 97 | .then((data) => { 98 | registerLibs(data, allLibs); 99 | }) 100 | .catch((err) => { 101 | log.debug('[link]', err); 102 | log.warn('[link]', `'${idePaths.idea.schemaRegistry}' doesn't exist. Creating...`); 103 | fs.copy(idePaths.idea.template, idePaths.idea.schemaRegistry) 104 | .then(() => { 105 | // recurse and start again from scratch 106 | idea(options, allLibs); 107 | }) 108 | .catch((copyErr) => { 109 | log.error('[link]', copyErr); 110 | }); 111 | }); 112 | }); 113 | } 114 | 115 | 116 | module.exports = function link(options, allLibs) { 117 | idea(options, allLibs); 118 | }; 119 | -------------------------------------------------------------------------------- /lib/Options.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | const path = require('path'); 3 | const Minilog = require('minilog'); 4 | const request = require('request-promise'); 5 | const fs = require('fs-extra'); 6 | 7 | const Util = require('./Util'); 8 | 9 | const log = Minilog('ui5-schemas'); 10 | 11 | class Options { 12 | constructor(options) { 13 | this.options = options; 14 | 15 | if (this.options.origin) { 16 | // ignore sdk/version if origin is provided 17 | delete this.options.version; 18 | delete this.options.sdk; 19 | } else if (this.options.sdk === 'openui5nightly') { 20 | // ignore version if openui5nightly 21 | delete this.options.version; 22 | } 23 | } 24 | 25 | async initialize() { 26 | await this.determineVersion(); 27 | } 28 | 29 | async determineVersion() { 30 | if (!this.options.origin && this.version) { 31 | return; 32 | } 33 | 34 | let versionOrigin; 35 | if (this.options.origin) { 36 | log.info(`Determining version from origin '${this.options.origin}' ...`); 37 | versionOrigin = `${this.options.origin}/resources/sap-ui-version.json`; 38 | } else if (!this.version) { 39 | log.info(`Attempting to download latest version of '${this.sdk}'. Determining latest version...`); 40 | versionOrigin = `${this.sdkBaseURI}/resources/sap-ui-version.json`; 41 | } 42 | 43 | const sapUiVersion = await Options.loadVersion(versionOrigin); 44 | this.version = sapUiVersion.version; 45 | 46 | // also determine sdk from download if using origin 47 | if (this.options.origin) { 48 | this.sdk = sapUiVersion.name.toLowerCase().indexOf('sapui5') !== -1 ? 'sapui5' : 'openui5'; 49 | } 50 | 51 | log.info(`Detected version of sdk '${this.sdk}' is '${this.version}'`); 52 | } 53 | 54 | static async loadVersion(versionOrigin) { 55 | log.info(versionOrigin); 56 | if (versionOrigin.startsWith('http')) { 57 | return JSON.parse(await request(versionOrigin)); 58 | } 59 | return fs.readJSON(versionOrigin); 60 | } 61 | 62 | get origin() { 63 | return this.options.version; 64 | } 65 | 66 | set origin(version) { 67 | this.options.version = version; 68 | } 69 | 70 | get version() { 71 | return this.options.version; 72 | } 73 | 74 | set version(version) { 75 | this.options.version = version; 76 | } 77 | 78 | isSnapshotVersion() { 79 | return this.options.version.endsWith('-SNAPSHOT'); 80 | } 81 | 82 | set sdk(sdk) { 83 | this.options.sdk = sdk; 84 | } 85 | 86 | get sdk() { 87 | return this.options.sdk; 88 | } 89 | 90 | get upgrade() { 91 | return this.options.upgrade; 92 | } 93 | 94 | get link() { 95 | return this.options.link; 96 | } 97 | 98 | get debug() { 99 | return this.options.debug; 100 | } 101 | 102 | get schemaDir() { 103 | return 'downloads/schemas'; 104 | } 105 | 106 | get schemaBaseUrl() { 107 | const schemaPath = this.isSnapshotVersion() 108 | ? this.schemaDir : path.posix.join(this.options.version, this.schemaDir); 109 | return `${this.sdkBaseURI}/${schemaPath}`; 110 | } 111 | 112 | get schemaTargetBasePath() { 113 | const sdk = this.sdk === 'openui5nightly' ? 'openui5' : this.sdk; 114 | return path.join(this.ui5SchemasBaseDir, sdk, this.version); 115 | } 116 | 117 | get ui5SchemasBaseDir() { 118 | return path.join(Util.getAppDataDir(), 'ui5experts', 'ui5-schemas'); 119 | } 120 | 121 | get allLibsPath() { 122 | if (this.isSnapshotVersion()) { 123 | path.join(this.ui5SchemasBaseDir, this.sdk, 'all_libs'); 124 | } 125 | return path.join(this.ui5SchemasBaseDir, this.sdk, this.version, 'all_libs'); 126 | } 127 | 128 | get allLibsUrl() { 129 | let allLibsPath; 130 | if (this.isSnapshotVersion()) { 131 | allLibsPath = this.allLibsMetafile; 132 | } else { 133 | allLibsPath = path.posix.join(this.options.version, this.allLibsMetafile); 134 | } 135 | return `${this.sdkBaseURI}/${allLibsPath}`; 136 | } 137 | 138 | get allLibsMetafile() { 139 | return 'discovery/all_libs'; 140 | } 141 | 142 | get sdkBaseURI() { 143 | if (this.options.sdk === 'sapui5') { 144 | return 'https://sapui5.hana.ondemand.com'; 145 | } if (this.options.sdk === 'openui5nightly') { 146 | return 'https://openui5nightly.hana.ondemand.com'; 147 | } 148 | return 'https://openui5.hana.ondemand.com'; 149 | } 150 | } 151 | 152 | module.exports = Options; 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./docs/ui5-schemas.png) 2 | 3 | [![npm version](https://img.shields.io/npm/v/ui5-schemas.svg)](https://www.npmjs.com/package/ui5-schemas) 4 | [![Build Status](https://travis-ci.org/ui5experts/ui5-schemas.svg?branch=master)](https://travis-ci.org/ui5experts/ui5-schemas) 5 | [![bitHound Overall Score](https://www.bithound.io/github/ui5experts/ui5-schemas/badges/score.svg)](https://www.bithound.io/github/ui5experts/ui5-schemas) 6 | [![License: MIT](https://img.shields.io/github/license/mashape/apistatus.svg)]() 7 | 8 | 9 | ## ⚠️ NOTE 10 | 11 | As [SAP sunsetted the support for the SAPUI5 Tools for Eclipse](https://blogs.sap.com/2019/11/26/sapui5-tools-for-eclipse-now-is-the-time-to-look-for-alternatives/) they also removed the XSD schemas from the project. ui5-schemas obviously can not work without those schemas. If you want to continue using ui5-schemas keep using the latest available version of SAPUI5/OpenUI5 that includes schemas which is *1.71*. 12 | 13 | I'm working together with the SAP colleagues to potentially find an alternative solution to keep this project alive. Stay tuned! 14 | 15 | More detailed information on the issue in [sap/openui5#2751](https://github.com/SAP/openui5/issues/2751) resp. [ui5experts/ui5-schemas#47](https://github.com/ui5experts/ui5-schemas/issues/47) 16 | 17 | ## What is UI5 Schemas? 18 | 19 | UI5 Schemas allows you to develop SAPUI5/OpenUI5 XML at a maximum convenience. It downloads, upgrades and sets 20 | up SAPUI5/OpenUI5 XML schemas for a better development experience in your favorite IDE (if it is WebStorm ;). 21 | 22 | ![](./docs/xml-code-completion.gif) 23 | 24 | 25 | ## Getting Started 26 | 27 | ```sh 28 | $ npm install -g ui5-schemas 29 | ``` 30 | 31 | ```sh 32 | $ ui5-schemas 33 | ``` 34 | 35 | npm 5: 36 | ```sh 37 | $ npx ui5-schemas 38 | ```` 39 | 40 | Use cli option ``--help`` for more details 41 | 42 | ```sh 43 | $ ui5-schemas --help 44 | 45 | Usage: ui5-schemas [options] 46 | 47 | Options: 48 | --origin The src url (sdk base url) or path (sdk root dir) to be used 49 | for schema loading. [string] 50 | --sdk The sdk to be used. 51 | [string] [choices: "sapui5", "openui5", "openui5nightly"] [default: "sapui5"] 52 | --version, -v The UI5 version to be used, defaults to '' which means latest. 53 | [string] [default: ""] 54 | --upgrade Whether to upgrade UI5 schemas for a better development 55 | experience or leave them untouched. 56 | [boolean] [default: true] 57 | --link Whether to auto-link UI5 schemas with your favorite IDE (if it 58 | is WebStorm ;). [boolean] [default: true] 59 | --debug Whether to show debug output 60 | [boolean] [default: false] 61 | -h, --help Show help 62 | 63 | Examples: 64 | ui5-schemas --sdk openui5 --version 1.28.15 Setup with openui5 schemas in version 1.28.15 65 | ui5-schemas --sdk openui5nightly Setup with openui5nightly 66 | ui5-schemas --origin '/Users/cschuff/Downloads/sapui5-sdk-1.65.1' 67 | Setup schemas from local sdk download 68 | ui5-schemas --origin 'https://my.abap.system/sap/public/bc/ui5_ui5' 69 | Setup schemas from sdk on an ABAP system 70 | ui5-schemas --no-upgrade Setup schemas without schema enhancement 71 | npx ui5-schemas --sdk openui5 --version 1.28.15 NPM5 72 | ``` 73 | 74 | 75 | ## Features 76 | 77 | ### Custom Origin 78 | Install schemas from any origin: Use the CDN, a downloaded SDK or even your own remote system. 79 | 80 | ### Multi-SDK Support 81 | Use the 'OpenUI5', 'OpenUI5 Nightly' or 'SAPUI5' SDK for your project. 82 | 83 | ### Multi-Version Support 84 | Use any available OpenUI5/SAPUI5 version in your project. Switch versions with ease! 85 | 86 | 87 | ## Known Limitations 88 | 89 | The xml schema files provided by SAP come with some limitations that this module does not fix (yet): 90 | * Binding syntax is not supported ([#3](https://github.com/ui5experts/ui5-schemas/issues/3)) 91 | * Aggregation Bindings that can be written as properties ([#29](https://github.com/ui5experts/ui5-schemas/issues/29)) 92 | * ... 93 | 94 | 95 | ## Usage behind Proxies 96 | 97 | Proxies used according to [npm module request](https://www.npmjs.com/package/request#proxies). Make sure to have set the following env vars: 98 | * HTTP_PROXY / http_proxy 99 | * HTTPS_PROXY / https_proxy 100 | * NO_PROXY / no_proxy 101 | 102 | If you are behind a corporate proxy and experience 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' it is most probably due to an invalid proxy certificate. You can still run ui5-schemas like this at your own risk: 103 | ```sh 104 | NODE_TLS_REJECT_UNAUTHORIZED=0 ui5-schemas 105 | # windows 106 | set NODE_TLS_REJECT_UNAUTHORIZED=0 107 | ui5-schemas 108 | ``` 109 | 110 | If none of this works just download a SAPUI5/OpenUI5 SDK and install schemas from the filesystem: 111 | ```sh 112 | $ ui5-schemas --origin '/Users/cschuff/Downloads/sapui5-sdk-1.65.1' 113 | ``` 114 | 115 | 116 | ## What is yet to come? 117 | 118 | * Eclipse support 119 | * Feel free to [open an issue](https://github.com/ui5experts/ui5-schemas/issues/new) if you are missing something else! 120 | -------------------------------------------------------------------------------- /lib/upgrade.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const xml2js = require('xml2js'); 3 | const Minilog = require('minilog'); 4 | 5 | const parser = new xml2js.Parser(); 6 | const builder = new xml2js.Builder(); 7 | const log = Minilog('ui5-schemas'); 8 | 9 | // Regex Aggregation Binding, simple binding: {(.*\n)*.*} 10 | // no need to change xsd:string 11 | 12 | function createAttributeJSON(name, type, docs) { 13 | return { 14 | $: { 15 | name, 16 | type, 17 | }, 18 | 'xsd:annotation': [ 19 | { 20 | 'xsd:documentation': [docs], 21 | }, 22 | ], 23 | }; 24 | } 25 | 26 | // TODO: Add support for 'binding' and 'objectBindings' (see https://github.com/SAP/openui5/blob/bbc8652c94512f237c55014cdb928fb56fa6aafa/src/sap.ui.core/src/sap/ui/core/XMLTemplateProcessor.js) 27 | function sapUiCoreView(data) { 28 | log.debug('[enhance]', 'Improving sap.ui.core.View'); 29 | const view = data['xsd:schema']['xsd:complexType'].find(element => element.$.name === '_ViewType'); 30 | const viewAttributes = view['xsd:complexContent'][0]['xsd:extension'][0]['xsd:attribute']; 31 | if (viewAttributes.filter(attr => attr.$.name === 'controllerName').length === 0) { 32 | viewAttributes.push(createAttributeJSON('controllerName', 'xsd:string', 33 | 'Name of the controller class to use for this view.')); 34 | } 35 | if (viewAttributes.filter(attr => attr.$.name === 'resourceBundleName').length === 0) { 36 | viewAttributes.push(createAttributeJSON('resourceBundleName', 'xsd:string', 37 | '(module) Name of a resource bundle that should be loaded for this view')); 38 | } 39 | if (viewAttributes.filter(attr => attr.$.name === 'resourceBundleUrl').length === 0) { 40 | viewAttributes.push(createAttributeJSON('resourceBundleUrl', 'xsd:string', 41 | 'URL of a resource bundle that should be loaded for this view')); 42 | } 43 | if (viewAttributes.filter(attr => attr.$.name === 'resourceBundleLocale').length === 0) { 44 | viewAttributes.push(createAttributeJSON('resourceBundleLocale', 'xsd:string', 45 | 'Locale that should be used to load a resource bundle for this view')); 46 | } 47 | if (viewAttributes.filter(attr => attr.$.name === 'resourceBundleAlias').length === 0) { 48 | viewAttributes.push(createAttributeJSON('resourceBundleAlias', 'xsd:string', 49 | 'Model name under which the resource bundle should be stored.')); 50 | } 51 | } 52 | 53 | 54 | function sapUiCoreControl(data) { 55 | log.debug('[enhance]', 'Improving sap.ui.core.Control'); 56 | 57 | // add binding type: 58 | // 59 | // 60 | // 61 | // 62 | // 63 | data['xsd:schema']['xsd:simpleType'].push({ 64 | $: { 65 | name: '_UI5ExpertsSimpleBindingType', 66 | }, 67 | 'xsd:restriction': [{ 68 | $: { 69 | base: 'xsd:string', 70 | }, 71 | 'xsd:pattern': [{ 72 | $: { 73 | value: '{.*}', 74 | }, 75 | }], 76 | }], 77 | }); 78 | 79 | const control = data['xsd:schema']['xsd:complexType'] 80 | .find(element => element.$.name === '_ControlType'); 81 | const controlAttributes = control['xsd:complexContent'][0]['xsd:extension'][0]['xsd:attribute']; 82 | if (controlAttributes.filter(attr => attr.$.name === 'class').length === 0) { 83 | controlAttributes.push(createAttributeJSON('class', 'xsd:string', 'A string that will be added ' 84 | + 'to the "class" attribute of this control\'s root HTML element.')); 85 | } 86 | if (controlAttributes.filter(attr => attr.$.name === 'binding').length === 0) { 87 | controlAttributes.push(createAttributeJSON('binding', 'xsd:string', 'Bind the object to the ' 88 | + 'referenced entity in the model, which is used as the binding context to resolve bound ' 89 | + 'properties or aggregations of the object itself and all of its children relatively to the ' 90 | + 'given path.')); 91 | } 92 | } 93 | 94 | 95 | module.exports = function upgrade(options, allLibs) { 96 | const allLibsRegistry = allLibs.all_libs; 97 | log.info('[enhance]', 'Making schemas even cooler...'); 98 | 99 | const sapUiCore = allLibsRegistry.find(element => element.entry === 'sap/ui/core'); 100 | 101 | fs.readFile(sapUiCore.location, 'utf-8') 102 | .then((data) => { 103 | parser.parseString(data, (err, result) => { 104 | if (err) { 105 | log.error('[enhance]', err); 106 | return; 107 | } 108 | sapUiCoreView(result); 109 | sapUiCoreControl(result); 110 | 111 | fs.outputFile(sapUiCore.location, builder.buildObject(result)) 112 | .then(() => { 113 | log.info('[enhance]', 'Enhancing done!'); 114 | }) 115 | .catch(() => { 116 | }); 117 | }); 118 | }) 119 | .catch((err) => { 120 | log.error('[enhance]', err); 121 | }); 122 | 123 | 124 | // TODO: add aggregations as string properties (to allow for bindings) 125 | // TODO: add type string to any non-string property (to allow for bindings) 126 | // TODO: create a 'binding type' 127 | // TODO: add core:FragmentDefinition 128 | // TODO: add tooltip string property 129 | // TODO: Split up libs in separate sub-package schemas 130 | // https://regex101.com/r/9dtZZG/1 131 | 132 | // 133 | // 134 | // 135 | // 136 | // 137 | 138 | // 139 | // https://github.com/SAP/openui5/blob/e4416cb9e2bbc05ac76a64d79ea49b1fb5ca56a3/src/sap.ui.core/src/sap/ui/core/XMLTemplateProcessor.js 140 | // https://github.com/SAP/openui5/blob/e4416cb9e2bbc05ac76a64d79ea49b1fb5ca56a3/src/sap.ui.core/src/sap/ui/base/BindingParser.js 141 | // https://github.com/SAP/openui5/blob/e4416cb9e2bbc05ac76a64d79ea49b1fb5ca56a3/src/sap.ui.core/src/sap/ui/base/ExpressionParser.js 142 | }; 143 | --------------------------------------------------------------------------------