UNPKG

@sap/cds-dk

Version:

Command line client and development toolkit for the SAP Cloud Application Programming Model

173 lines (145 loc) 6.73 kB
const util = require('util') const { exec } = require('child_process') const execAsync = util.promisify(exec) const term = require('../util/term') const cmd = require('../util/command') const cds = require('../cds') const LOG = cds.log('cli|init|add|mvn'), DEBUG = cds.debug('cli|init|add|mvn') const { URLS, MAVEN_ARCHETYPE_VERSION, OPTIONS: { JAVA_PACKAGE, HANA }, REGEX_JAVA_PACKAGE } = require('./constants') module.exports.add = async function (feature, featureOptions={}) { const params = new Map() let archetypeVersion = MAVEN_ARCHETYPE_VERSION params.set(`com.sap.cds:cds-maven-plugin:${archetypeVersion}:add`) if (feature === HANA) { // REVISIT: compat for HANA until -Dfeature HANA is provided by Java feature = `CF` // REVISIT: Should get rid of CF dependency for Kyma scenarios } params.set(`-Dfeature`, feature) const profile = cds.cli.options.for params.set(`-Dprofile`, profile === 'production' ? 'cloud' : profile ?? 'default') Object.entries(featureOptions).forEach(([k,v]) => params.set(`-Doptions.${k}`, v)) // evaluate last to allow generic override from CLI const { 'java:mvn': javaMvn, force } = cds.cli.options if (javaMvn) _scan(javaMvn).forEach(([k,v]) => params.set(k,v)) if (force) params.set(`-Doverwrite`, true) try { return await cmd.spawnCommand('mvn', _finalize(params), { cwd: cds.root }, false, true) } catch (err) { if (err.code === 'ENOENT' && err.path === 'mvn') { throw `Maven executable 'mvn' not found. Follow ${term.info(URLS.MAVEN_INSTALL_HELP)} and install Maven on your machine.` } throw err } } module.exports.init = async function (projectName, options = cds.cli.options) { const params = new Map() // set header params.set(`archetype:generate`); params.set(`-DarchetypeArtifactId`, `cds-services-archetype`); params.set(`-DarchetypeGroupId`, `com.sap.cds`); params.set(`-DarchetypeVersion`, MAVEN_ARCHETYPE_VERSION); params.set('-DinteractiveMode', false); if (cds.cli.options['no-db']) params.set('-Dpersistence', false) // set defaults, might be changed by java:mvn param params.set(`-DartifactId`, projectName); params.set(`-DgroupId`, 'customer'); const { 'java:mvn': javaMvn } = options if (javaMvn) _scan(javaMvn).forEach(([k,v]) => params.set(k,v)) // -DjdkVersion=[11|17] if (!params.get('-DjdkVersion')) { let javaVersion try { javaVersion = await execAsync('java -version'); } catch (err) { DEBUG?.(err); } const [,version] = javaVersion?.stderr?.match(/version\s*"?(\d+\.\d+\.\d+)"?/) ?? []; if (version) { const [major] = version.split('.').map(Number); if (major < 17) { if (process.env.WS_BASE_URL) { throw `Java version ${version} is not supported. Use Java 17 or higher. You can use command 'Java: Set Default JDK' to switch to Java 17.` } throw `Java version ${version} is not supported. Use Java 17 or higher.` } } } if (options[JAVA_PACKAGE]) { params.set(`-Dpackage`, options[JAVA_PACKAGE]); } const packageFromParam = params.get(`-Dpackage`); if (packageFromParam) { // explicitly specified, so do not modify if (!options.force && !packageFromParam.match(REGEX_JAVA_PACKAGE)) { throw `Package '${packageFromParam}' is an invalid Java package name. Use --force to use it anyway.` } } else { // potentially overwritten by mvn param const groupId = params.get(`-DgroupId`); let packageName = `${groupId}.${projectName}`; if (!packageName.match(REGEX_JAVA_PACKAGE)) { packageName = sanitize(packageName); console.warn(`The derived package name '${groupId}.${projectName}' is not a valid Java package name. Using '${packageName}' instead.`); } params.set(`-Dpackage`, packageName); } const artifactId = params.get('-DartifactId') const archetypeVersion = params.get('-DarchetypeVersion') return { params: _finalize(params), archetypeVersion, artifactId } } module.exports.generate = async function (feature, options = cds.cli.options) { const command = this.generateMvnParams(feature, options) const cwd = options.cwd ?? cds.root try { console.log(`executing: `, term.highlight('mvn ' + command.join(' '))) await cmd.spawnCommand('mvn', command, { cwd: cwd }) return command } catch (err) { if (err.code === 'ENOENT' && err.path === 'mvn') { throw `Maven executable 'mvn' not found. Follow ${term.info(URLS.MAVEN_INSTALL_HELP)} and install Maven on your machine.` } throw err } } module.exports.generateMvnParams = function (feature, options = cds.cli.options) { const params = new Map() const { 'java:mvn': javaMvn, force, filter, out } = options if (javaMvn) _scan(javaMvn).forEach(([k,v]) => params.set(k,v)) const archVersion = params.get(`-DarchetypeVersion`) ?? '' params.set(`com.sap.cds:cds-maven-plugin:${archVersion}:generate`) switch (feature) { case 'handler': params.set(`-Dcds.generate.feature=EVENT_HANDLERS`) break } if (filter) params.set(`-Dcds.generate.includes=**.${filter.replace(/\*+\.?/, '')}**`) if (out) params.set(`-Dcds.generate.handlers.package=${out.replaceAll('/', '.')}`) if (force) params.set(`-Dcds.generate.overwrite`, true) return _finalize(params) } const sanitize = module.exports.sanitize = name => name .toLowerCase() .replace(/-/g, '_') .replace(/\.([0-9])/g, '._$1') .replace(/^([0-9])/, '_$1') .replace(/\.+/g, '.') function _scan(mvnParams) { // need to create new regex every call since a constant // would keep the match state // captures a=1 => a:1 // -Da=[x,y,z] => -Da:x,y,z (Maven specific) // a=[x,y,z] => a:x,y,z const quoteRegex = /([\w\-.]+)=([\w\-.]+|\[([\w.\-,]+)\])/g return [...mvnParams.matchAll(quoteRegex)].map(([,key,value,listValues]) => { key = key.startsWith('-D') ? key : `-D${key}` return [key, listValues || value] }) } function _finalize(params) { params.set(`-Dstyle.color`, `always`) if (LOG._debug) params.set('-X') if (LOG._debug) params.set('-e') if (process.env.CI) params.set('--batch-mode') const final = Array.from(params, ([arg, val]) => val !== null && val !== undefined ? `${arg}=${val}` : arg) DEBUG?.(term.highlight('mvn ' + final.join(' '))) return final }