@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
173 lines (145 loc) • 6.73 kB
JavaScript
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
}