@sap/cds-dk 7.6.1 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cds.js +1 -1
- package/bin/version.js +21 -20
- package/bin/watch.js +6 -0
- package/lib/build/buildTaskEngine.js +6 -4
- package/lib/build/buildTaskProviderFactory.js +5 -0
- package/lib/build/provider/edmxBuildPlugin.js +1 -1
- package/lib/build/provider/internalBuildTaskProvider.js +12 -10
- package/lib/build/provider/mtx/index.js +2 -2
- package/lib/build/provider/mtx/resourcesTarBuilder.js +1 -1
- package/lib/build/provider/mtx-extension/extensionCompilation.js +50 -0
- package/lib/build/provider/mtx-extension/index.js +50 -48
- package/lib/build/provider/mtx-sidecar/index.js +3 -3
- package/lib/build/util.js +9 -4
- package/lib/env/schema.js +1 -0
- package/lib/env/schemas/cds-rc.js +54 -1
- package/lib/import/asyncapi/parserContext.js +6 -0
- package/lib/init/add.js +2 -0
- package/lib/init/constants.js +1 -1
- package/lib/init/merge.js +31 -13
- package/lib/init/projectReader.js +9 -1
- package/lib/init/registries/mta.js +19 -1
- package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +5 -5
- package/lib/init/template/connectivity/index.js +3 -4
- package/lib/init/template/html5-repo/files/app-package.json.hbs +16 -0
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +83 -0
- package/lib/init/template/html5-repo/files/package.json.hbs +17 -0
- package/lib/init/template/html5-repo/files/ui5.yaml.hbs +33 -0
- package/lib/init/template/html5-repo/files/xs-app.json.hbs +19 -0
- package/lib/init/template/html5-repo/index.js +56 -12
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -0
- package/lib/init/template/mta/index.js +5 -0
- package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -2
- package/lib/init/template/nodejs/files/README.md +1 -1
- package/lib/init/template/sample/files/app/index.html +8 -6
- package/lib/init/template/sample/index.js +12 -2
- package/lib/util/cf.js +1 -0
- package/npm-shrinkwrap.json +302 -276
- package/package.json +1 -1
package/bin/cds.js
CHANGED
|
@@ -77,7 +77,7 @@ const cli = {
|
|
|
77
77
|
args (task, argv) {
|
|
78
78
|
|
|
79
79
|
const { options:o=[], flags:f=[], shortcuts:s=[] } = task
|
|
80
|
-
const _global = /^--(profile|production|sql|odata|build-.*|cdsc-.*|odata-.*|folders-.*)$/
|
|
80
|
+
const _global = /^--(profile|production|sql|odata|add-.*|build-.*|cdsc-.*|odata-.*|folders-.*)$/
|
|
81
81
|
const _flags = { '--production':true }
|
|
82
82
|
const options = {}, args = []
|
|
83
83
|
let k,a, env = null
|
package/bin/version.js
CHANGED
|
@@ -89,30 +89,31 @@ function _versions_from(pkg_path, pkg_label) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function _markdown (versions) {
|
|
92
|
-
console.log()
|
|
93
92
|
const pkg = { name:'', repository:'', version:'' }; try {
|
|
94
93
|
Object.assign (pkg, require (resolve('package.json')))
|
|
95
94
|
} catch (e) {/* ignored */}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return (component + ' ').slice(0,22)
|
|
113
|
-
+' | '+ (version + ' ').slice(0,11)
|
|
114
|
-
}
|
|
95
|
+
const repo = pkg.repository.url || pkg.repository || 'https://github.com/<your/repo>'
|
|
96
|
+
const valLength = Math.max(repo.length, versions.home.length)
|
|
97
|
+
|
|
98
|
+
console.log ()
|
|
99
|
+
console.log ('|', $(pkg.name, repo), '|')
|
|
100
|
+
console.log ('|', $('----------------------', '-'.repeat(valLength)), '|')
|
|
101
|
+
|
|
102
|
+
Object.keys(versions)
|
|
103
|
+
.filter(mod => mod != pkg.name)
|
|
104
|
+
.sort()
|
|
105
|
+
.forEach(id => console.log ('|', $(id, versions[id]), '|'))
|
|
106
|
+
|
|
107
|
+
// TODO get CAP Java version from mvn
|
|
108
|
+
// if (require('../lib').env['project-nature'] === 'java') {
|
|
109
|
+
// console.log ('|', $('CAP Java Runtime'), '|')
|
|
110
|
+
// }
|
|
115
111
|
console.log()
|
|
112
|
+
|
|
113
|
+
function $ (key, val=MISSING) {
|
|
114
|
+
return ( key + ' '.repeat(22)).slice(0,22)
|
|
115
|
+
+' | '+ (val + ' '.repeat(valLength)).slice(0,valLength)
|
|
116
|
+
}
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
function _pkg_path(id, ...paths) {
|
package/bin/watch.js
CHANGED
|
@@ -78,6 +78,12 @@ async function watch ([cwd], {
|
|
|
78
78
|
ext = extDefaults,
|
|
79
79
|
...options
|
|
80
80
|
}={}) {
|
|
81
|
+
if (cwd && !cds.utils.exists(cwd)) try { // try module name like `@capire/bookshop`
|
|
82
|
+
cwd = require.resolve(cwd+'/package.json').slice(0,-13)
|
|
83
|
+
} catch (err) {
|
|
84
|
+
throw `Error: No such folder or package: '${cwd}'`
|
|
85
|
+
}
|
|
86
|
+
|
|
81
87
|
if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
|
|
82
88
|
if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
|
|
83
89
|
args
|
|
@@ -9,6 +9,7 @@ const InternalBuildPlugin = require('./provider/internalBuildPlugin')
|
|
|
9
9
|
const COMPILATION_ERROR = 'CompilationError'
|
|
10
10
|
const COMPILE_MESSAGE = 'CompileMessage'
|
|
11
11
|
const DEBUG = cds.debug('cli|build')
|
|
12
|
+
const LOG_LIMIT = 30
|
|
12
13
|
|
|
13
14
|
class BuildTaskEngine {
|
|
14
15
|
constructor(options) {
|
|
@@ -38,7 +39,7 @@ class BuildTaskEngine {
|
|
|
38
39
|
// validate required @sap namespace models - log only
|
|
39
40
|
const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
|
|
40
41
|
if (unresolved.length > 0) {
|
|
41
|
-
throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved.
|
|
42
|
+
throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Use the 'npm install' command to install up-to-date versions of the missing packages.`)
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
// create build plugins
|
|
@@ -221,9 +222,10 @@ class BuildTaskEngine {
|
|
|
221
222
|
const files = BuildTaskEngine._getOutput(plugins)
|
|
222
223
|
if (files.length > 0) {
|
|
223
224
|
const term = require('../util/term')
|
|
224
|
-
if (!process.env.DEBUG && files.length >
|
|
225
|
-
|
|
226
|
-
files.
|
|
225
|
+
if (!process.env.DEBUG && files.length > LOG_LIMIT) {
|
|
226
|
+
const length = files.length
|
|
227
|
+
files.length = LOG_LIMIT
|
|
228
|
+
files.push(`... ${length - files.length} more. Run with DEBUG=build to show all files.`)
|
|
227
229
|
}
|
|
228
230
|
console.log(`done > wrote output to:\n ${term.secondary(files.join("\n "))}\n`)
|
|
229
231
|
}
|
|
@@ -13,6 +13,11 @@ class BuildTaskProviderFactory {
|
|
|
13
13
|
if (options.clean === undefined) {
|
|
14
14
|
options.clean = true
|
|
15
15
|
}
|
|
16
|
+
['for', 'src', 'dest'].forEach(key => {
|
|
17
|
+
if (options[key] && typeof options[key] !== 'string') {
|
|
18
|
+
throw new BuildError(`Invalid build options - property '${key}' must be a string`)
|
|
19
|
+
}
|
|
20
|
+
})
|
|
16
21
|
this._buildOptions = options
|
|
17
22
|
this._context = { options, tasks: [] } // resolved tasks
|
|
18
23
|
}
|
|
@@ -52,7 +52,7 @@ class EdmxBuildPlugin extends FeatureToggleBuildPlugin {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
_isOdataProtocol(srv) {
|
|
55
|
-
if ('
|
|
55
|
+
let yes = srv._serves_odata; if (yes) return typeof yes === 'function' ? yes.call(srv) : yes // >= cds 7.4.0
|
|
56
56
|
let protocols = cds.service.protocols4?.(srv) // >= cds 7.4.0
|
|
57
57
|
if (!protocols) { // @sap/cds < 7.3.0
|
|
58
58
|
protocols = cds.service.protocols.protocol4(srv)
|
|
@@ -183,7 +183,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
183
183
|
if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
184
184
|
if (sidecarEnv.profiles?.includes('mtx-sidecar')) {
|
|
185
185
|
// profile has been set, but cannot be resolved
|
|
186
|
-
throw new BuildError(
|
|
186
|
+
throw new BuildError("MTX configuration cannot be resolved. Use the 'npm install' command to install up-to-date versions of @sap/cds-mtxs and @sap/cds.")
|
|
187
187
|
}
|
|
188
188
|
throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
|
|
189
189
|
}
|
|
@@ -264,26 +264,29 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
264
264
|
static _setDefaultSrcFolder(task) {
|
|
265
265
|
switch (task.for) {
|
|
266
266
|
case BUILD_TASK_HANA:
|
|
267
|
-
task.src
|
|
267
|
+
task.src ||= normalizePath(cds.env.folders.db)
|
|
268
268
|
break
|
|
269
269
|
case BUILD_TASK_JAVA:
|
|
270
270
|
case BUILD_TASK_JAVA_CF:
|
|
271
271
|
case BUILD_TASK_NODEJS:
|
|
272
272
|
case BUILD_TASK_NODE_CF:
|
|
273
|
-
task.src
|
|
273
|
+
task.src ||= normalizePath(cds.env.folders.srv)
|
|
274
274
|
break
|
|
275
275
|
case BUILD_TASK_FIORI:
|
|
276
|
-
task.src
|
|
276
|
+
task.src ||= normalizePath(cds.env.folders.app)
|
|
277
277
|
break
|
|
278
278
|
case BUILD_TASK_MTX_SIDECAR:
|
|
279
|
-
task.src
|
|
279
|
+
task.src ||= MTX_SIDECAR_FOLDER
|
|
280
280
|
break
|
|
281
281
|
case BUILD_TASK_MTX_EXTENSION:
|
|
282
|
-
task.src
|
|
282
|
+
if (task.src && task.src !== cds.root) {
|
|
283
|
+
throw new BuildError("Invalid 'src' property value for build task 'mtx-extension', the only allowed value is '.'")
|
|
284
|
+
}
|
|
285
|
+
task.src ||= "."
|
|
283
286
|
break
|
|
284
287
|
case BUILD_TASK_MTX:
|
|
285
288
|
// mtxs with nodejs, but without sidecar
|
|
286
|
-
task.src
|
|
289
|
+
task.src ||= normalizePath(cds.env.folders.srv)
|
|
287
290
|
break
|
|
288
291
|
default:
|
|
289
292
|
throw new Error(`Unknown build task '${task.use || task.for}'`)
|
|
@@ -296,8 +299,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
296
299
|
if (taskModelPaths && !Array.isArray(taskModelPaths)) {
|
|
297
300
|
taskModelPaths = [taskModelPaths]
|
|
298
301
|
}
|
|
299
|
-
const
|
|
300
|
-
const allowList = new RegExp(`^@sap/cds${toggles ? '|^' + toggles.replace('*', '') : ''}`)
|
|
302
|
+
const allowList = new RegExp(`^@sap/cds${cds.env.requires.toggles ? '|^' + cds.env.features.folders.replace('*', '') : ''}`)
|
|
301
303
|
task.options = task.options || {}
|
|
302
304
|
let defaultModelPaths = []
|
|
303
305
|
if (taskModelPaths?.length) {
|
|
@@ -336,7 +338,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
336
338
|
}
|
|
337
339
|
|
|
338
340
|
/**
|
|
339
|
-
* Determines the module folder from the
|
|
341
|
+
* Determines the module folder from the given list that may represent files or folders w or w/o .cds file extension.
|
|
340
342
|
* @param {Array} filesOrFolders
|
|
341
343
|
*/
|
|
342
344
|
static _getModuleFolder(filesOrFolders) {
|
|
@@ -3,7 +3,7 @@ const fs = require('fs')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const cds = require('../../../cds')
|
|
5
5
|
const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
6
|
-
const
|
|
6
|
+
const ResourcesTarBuilder = require('./resourcesTarBuilder')
|
|
7
7
|
const { FOLDER_GEN } = require('../../constants')
|
|
8
8
|
const { BuildError } = require('../../util')
|
|
9
9
|
|
|
@@ -29,7 +29,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
29
29
|
if (!model) {
|
|
30
30
|
return
|
|
31
31
|
}
|
|
32
|
-
return new
|
|
32
|
+
return new ResourcesTarBuilder(this).createTar(dest, model)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async clean() {
|
|
@@ -34,7 +34,7 @@ class ResourcesTarBuilder {
|
|
|
34
34
|
* If omitted all resources contained in the root directory are added to the TAR file.
|
|
35
35
|
*/
|
|
36
36
|
async writeTarFile(tarFile, root, resources) {
|
|
37
|
-
const { tar } = require('
|
|
37
|
+
const { tar } = require('../../..').utils
|
|
38
38
|
await tar.czfd(tarFile, root, resources)
|
|
39
39
|
this.plugin.pushFile(tarFile)
|
|
40
40
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const cds = require('../../../cds')
|
|
3
|
+
const { WARNING } = require('../internalBuildPlugin')
|
|
4
|
+
|
|
5
|
+
class ExtensionCompilation {
|
|
6
|
+
constructor(plugin) {
|
|
7
|
+
this._plugin = plugin
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get plugin() { return this._plugin }
|
|
11
|
+
|
|
12
|
+
isDuplicateDefinitionError(e) {
|
|
13
|
+
return e.code === 'ERR_CDS_COMPILATION_FAILURE' && e.messages?.some(m => m.messageId === 'duplicate-definition')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async recompileWithoutDuplicates(messages, saasPkgPath) {
|
|
17
|
+
// determine directories containing duplicate CDS definitions
|
|
18
|
+
const duplicates = messages?.reduce((acc, m) => {
|
|
19
|
+
if (m.messageId === 'duplicate-definition' && m.$location?.file) {
|
|
20
|
+
const dirname = path.resolve(cds.root, m.$location.file)
|
|
21
|
+
if (!dirname.startsWith(saasPkgPath)) {
|
|
22
|
+
acc.add(path.dirname(dirname))
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return acc
|
|
26
|
+
}, new Set())
|
|
27
|
+
|
|
28
|
+
function getPkgName(dirname) {
|
|
29
|
+
try {
|
|
30
|
+
if (dirname && dirname !== cds.root && path.dirname(dirname) !== dirname) {
|
|
31
|
+
const pkg = require(path.join(dirname, 'package.json'))
|
|
32
|
+
return pkg.name
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return getPkgName(path.dirname(dirname))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const pkgNames = [...[...duplicates].reduce((acc, dir) => acc.add(getPkgName(dir)), new Set())]
|
|
39
|
+
const modelPaths = this.plugin.task.options.model.filter(m => !pkgNames.some(pkg => m === pkg || m.startsWith(pkg + '/')))
|
|
40
|
+
this.plugin.task.options.model = modelPaths
|
|
41
|
+
this.plugin.pushMessage(`Duplicate CDS model definitions found. Using CDS artifacts from base model instead and recompiling with model options ${JSON.stringify(modelPaths)}.\nMake sure to use compatible versions for the dependencies '${pkgNames.join(', ')}'.`, WARNING)
|
|
42
|
+
// recompile without duplicates
|
|
43
|
+
return this.plugin.model()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
resolveExtensionFiles(model, saasPkgFolder) {
|
|
47
|
+
return model['$sources'].filter(file => file.startsWith(cds.root) && !file.startsWith(saasPkgFolder))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
module.exports = ExtensionCompilation
|
|
@@ -2,8 +2,9 @@ const path = require('path')
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
4
|
const InternalBuildPlugin = require('../internalBuildPlugin')
|
|
5
|
-
const { FOLDER_GEN, EXTENSION_POINT_VALIDATION } = require('../../constants')
|
|
6
5
|
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
6
|
+
const ExtensionCompilation = require('./extensionCompilation')
|
|
7
|
+
const { FOLDER_GEN, EXTENSION_POINT_VALIDATION } = require('../../constants')
|
|
7
8
|
const { BuildError, BuildMessage } = require('../../util')
|
|
8
9
|
const DEBUG = cds.debug('cli|build')
|
|
9
10
|
|
|
@@ -16,31 +17,44 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
async build() {
|
|
19
|
-
const {
|
|
20
|
+
const { dest } = this.task
|
|
20
21
|
const destExt = path.join(dest, 'ext')
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
let model
|
|
23
|
+
let extensionModel
|
|
24
|
+
|
|
25
|
+
// REVISIT: check existence of SaaS app package folder only
|
|
26
|
+
// cds.resolve will fail as no index.csn file exists in this folder for the extension migration use case
|
|
23
27
|
// a compilation error is thrown anyhow if any base model refs cannot be resolved
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
throw new BuildError(`The SaaS application base model '${appPackageFolder}' is missing. Have you run the 'cds pull' command?`)
|
|
27
|
-
}
|
|
28
|
+
const saasPkgPath = MtxExtensionBuildPlugin._getSaasPkgPath()
|
|
29
|
+
const extensionCompilation = new ExtensionCompilation(this)
|
|
28
30
|
|
|
29
31
|
// full compile of the extension using base model - ensuring consistency
|
|
30
|
-
|
|
32
|
+
try {
|
|
33
|
+
model = await this.model()
|
|
34
|
+
} catch (e) {
|
|
35
|
+
if (extensionCompilation.isDuplicateDefinitionError(e)) {
|
|
36
|
+
// clear compilation errors and recompile with removed duplicate resources
|
|
37
|
+
this.messages.length = 0
|
|
38
|
+
model = await extensionCompilation.recompileWithoutDuplicates(e.messages, saasPkgPath)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
|
|
32
42
|
if (model) {
|
|
33
43
|
// extension CSN using parsed format
|
|
34
44
|
const options = { ...this.options(), flavor: 'parsed' }
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
|
|
45
|
+
const extensionFiles = extensionCompilation.resolveExtensionFiles(model, saasPkgPath)
|
|
46
|
+
if (extensionFiles.length > 0) {
|
|
47
|
+
extensionModel = await cds.load(extensionFiles, options)
|
|
48
|
+
if (extensionModel.requires) {
|
|
49
|
+
extensionModel.requires.length = 0
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
|
-
|
|
52
|
+
}
|
|
53
|
+
if (extensionModel) {
|
|
40
54
|
// IMPORTANT NOTE: perform all model operations before the linter is called as the linter
|
|
41
55
|
// will modify the model causing the creation of 'unresolved' associations
|
|
42
|
-
await this.compileToJson(
|
|
43
|
-
await this.collectLanguageBundles(
|
|
56
|
+
await this.compileToJson(extensionModel, path.join(destExt, 'extension.csn'))
|
|
57
|
+
await this.collectLanguageBundles(extensionModel, path.join(destExt, 'i18n'))
|
|
44
58
|
|
|
45
59
|
// perform SQL backend validation to prevent extension deployment problems later on
|
|
46
60
|
// REVISIT: disabled - causing stack overflow errors in LinkedCSN for some scenarios
|
|
@@ -50,35 +64,30 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
|
50
64
|
if (files.length > 0) {
|
|
51
65
|
const dataDest = path.join(destExt, 'data')
|
|
52
66
|
await Promise.all(
|
|
53
|
-
files
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
return this.copy(csv).to(path.join(dataDest, path.basename(csv)))
|
|
57
|
-
})
|
|
67
|
+
files.filter(file => /\.csv$/.test(file)).map(csv => {
|
|
68
|
+
return this.copy(csv).to(path.join(dataDest, path.basename(csv)))
|
|
69
|
+
})
|
|
58
70
|
)
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
if (!this.hasBuildOption(EXTENSION_POINT_VALIDATION, false)) {
|
|
62
74
|
try {
|
|
63
75
|
// throws error in case of linting errors
|
|
64
|
-
this._lintExtModel(
|
|
76
|
+
this._lintExtModel(extensionModel, model, saasPkgPath)
|
|
65
77
|
} catch (error) {
|
|
66
78
|
// cleanup existing files in case of an error
|
|
67
|
-
await fs.promises.rm(
|
|
79
|
+
await fs.promises.rm(dest, { recursive: true })
|
|
68
80
|
throw error
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
throw new BuildError(`package.json file not found in ${src}`)
|
|
76
|
-
}
|
|
77
|
-
await this.copy(packageJson).to(path.join(destExt, 'package.json'))
|
|
85
|
+
// existence already checked
|
|
86
|
+
await this.copy(path.join(cds.root, 'package.json')).to(path.join(destExt, 'package.json'))
|
|
78
87
|
|
|
79
88
|
// copy handlers
|
|
80
|
-
const folders = [path.join(
|
|
81
|
-
await this.copyNativeContent(
|
|
89
|
+
const folders = [path.join(cds.root, cds.env.folders.srv, 'handlers')]
|
|
90
|
+
await this.copyNativeContent(cds.root, destExt, res => {
|
|
82
91
|
if (fs.statSync(res).isDirectory()) {
|
|
83
92
|
return folders.some(folder => folder.startsWith(res))
|
|
84
93
|
}
|
|
@@ -88,27 +97,15 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
|
88
97
|
})
|
|
89
98
|
|
|
90
99
|
// add all resources contained in 'gen/ext' folder
|
|
91
|
-
|
|
100
|
+
return new ResourcesTarBuilder(this).writeTarFile(path.join(dest, 'extension.tgz'), destExt)
|
|
92
101
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const node_modules = path.join(this.task.src, 'node_modules')
|
|
96
|
-
const paths = model['$sources'].reduce((acc, file) => {
|
|
97
|
-
if (file.startsWith(this.task.src) && !file.startsWith(node_modules)) {
|
|
98
|
-
acc.push(file)
|
|
99
|
-
}
|
|
100
|
-
return acc
|
|
101
|
-
}, [])
|
|
102
|
-
|
|
103
|
-
return paths
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
_lintExtModel(extModel, model) {
|
|
102
|
+
|
|
103
|
+
_lintExtModel(extModel, model, saasPkgPath) {
|
|
107
104
|
const linter = this._linter()
|
|
108
105
|
if (!linter) {
|
|
109
106
|
return
|
|
110
107
|
}
|
|
111
|
-
const env = cds.env.for('cds',
|
|
108
|
+
const env = cds.env.for('cds', saasPkgPath)
|
|
112
109
|
DEBUG?.(`Saas extension point restrictions:\n${env.requires?.['cds.xt.ExtensibilityService']}`)
|
|
113
110
|
|
|
114
111
|
const messages = linter.lint(extModel, model, env)
|
|
@@ -134,9 +131,14 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
|
134
131
|
}
|
|
135
132
|
return linter
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
|
|
135
|
+
static _getSaasPkgPath() {
|
|
136
|
+
// cds.env.extends holds the SaaS app package name, also if the extends field is defined on root level
|
|
137
|
+
const saasPkgPath = path.join(cds.root, 'node_modules', cds.env.extends || '_base')
|
|
138
|
+
if (!fs.existsSync(saasPkgPath)) {
|
|
139
|
+
throw new BuildError(`The SaaS application base model '${path.relative(cds.root, saasPkgPath)}' is missing. Did you run the 'cds pull' command?`)
|
|
140
|
+
}
|
|
141
|
+
return saasPkgPath
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -3,7 +3,7 @@ const cds = require('../../../cds')
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION, CONTENT_EDMX, FLAVOR_LOCALIZED_EDMX } = require('../../constants')
|
|
5
5
|
const NodejsBuildPlugin = require('../nodejs')
|
|
6
|
-
const
|
|
6
|
+
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
7
7
|
const { ERROR } = NodejsBuildPlugin
|
|
8
8
|
const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
|
|
9
9
|
const DEBUG = cds.debug('cli|build')
|
|
@@ -64,7 +64,7 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
64
64
|
async _buildMainApp(sidecarEnv) {
|
|
65
65
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
66
66
|
if (!main) {
|
|
67
|
-
throw new BuildError(
|
|
67
|
+
throw new BuildError("Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured. Use 'npm install @sap/cds-mtxs' to install an up-to-date version.")
|
|
68
68
|
}
|
|
69
69
|
if (!this.hasBuildOption(MTX_SIDECAR_DB_VALIDATION, false)
|
|
70
70
|
&& !cds.env.build.tasks?.length // disable if custom build tasks exist
|
|
@@ -97,7 +97,7 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
97
97
|
|
|
98
98
|
// create resources TAR
|
|
99
99
|
// resources are determined based on available database build task, SQLite as fallback
|
|
100
|
-
await new
|
|
100
|
+
await new ResourcesTarBuilder(this).createTar(destMain, model)
|
|
101
101
|
|
|
102
102
|
// copy package.json and .cdsrc.json from project root
|
|
103
103
|
await this._copyMainConfigFiles(cds.root, destMain)
|
package/lib/build/util.js
CHANGED
|
@@ -119,7 +119,7 @@ function resolveRequiredSapModels(modelPaths) {
|
|
|
119
119
|
|
|
120
120
|
async function getDefaultModelOptions(ws = false) {
|
|
121
121
|
const wildcards = cds.env.requires.toggles && cds.env.features.folders ? ['*', cds.env.features.folders] : '*'
|
|
122
|
-
let paths =
|
|
122
|
+
let paths = _filterModelPaths(cds.resolve(wildcards, false))
|
|
123
123
|
|
|
124
124
|
if (ws) {
|
|
125
125
|
const workspaces = await getWorkspaces()
|
|
@@ -129,7 +129,7 @@ async function getDefaultModelOptions(ws = false) {
|
|
|
129
129
|
workspaces.forEach(workspace => {
|
|
130
130
|
cds.root = path.join(root, workspace)
|
|
131
131
|
cds.env = cds.env.for('cds', cds.root)
|
|
132
|
-
paths = paths.concat(
|
|
132
|
+
paths = paths.concat(_filterModelPaths(cds.resolve(cds.env.requires.toggles ? wildcards : '*', false)).map(p => path.join(workspace, p)))
|
|
133
133
|
})
|
|
134
134
|
} finally {
|
|
135
135
|
cds.root = root
|
|
@@ -247,7 +247,7 @@ function _getWorkspace(wsRoot, pkg) {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
function _validateWsDependencies(wsRoot, dependencies, devDependencies) {
|
|
250
|
-
const allDependencies = {...dependencies, ...devDependencies}
|
|
250
|
+
const allDependencies = { ...dependencies, ...devDependencies }
|
|
251
251
|
for (const name in allDependencies) {
|
|
252
252
|
if (allDependencies[name] === '*') {
|
|
253
253
|
try {
|
|
@@ -262,7 +262,12 @@ function _validateWsDependencies(wsRoot, dependencies, devDependencies) {
|
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
/**
|
|
266
|
+
* Returns a flat array of existing files and folders, trailing '/' or '\\' characters have been removed.
|
|
267
|
+
* @param {...any} modelPaths array of files and folders - optionally nested
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
270
|
+
function _filterModelPaths(...modelPaths) {
|
|
266
271
|
const model = new Set()
|
|
267
272
|
// may contain nested arrays
|
|
268
273
|
modelPaths = flatten(modelPaths)
|
package/lib/env/schema.js
CHANGED
|
@@ -65,6 +65,7 @@ _robustRequire.resolve = function (id, options = {}) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function mergePluginSchema(pluginSchema) {
|
|
68
|
+
// contribution points also appear in schema file ./schemas/cds-rc.js
|
|
68
69
|
return {
|
|
69
70
|
into: async (targetSchema) => {
|
|
70
71
|
if (pluginSchema.buildTaskType) {
|
|
@@ -5,6 +5,59 @@ module.exports = {
|
|
|
5
5
|
$defs: {
|
|
6
6
|
cdsRoot: {
|
|
7
7
|
properties: {
|
|
8
|
+
// declaration for schema node for cds plugins contributing cds configuration
|
|
9
|
+
schema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
description: "Schema declaration for cds plugins contributing cds configuration",
|
|
12
|
+
additionalProperties: false,
|
|
13
|
+
properties: {
|
|
14
|
+
cds: {
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "Containing json schema declarations for root level entries under cds node.",
|
|
17
|
+
patternProperties: {
|
|
18
|
+
// new keyword must start with word character or opening brackets
|
|
19
|
+
"^[\\[\\(\\w].*": {
|
|
20
|
+
$ref: "https://json-schema.org/draft/2020-12/schema"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
buildTaskType: {
|
|
26
|
+
type: "object",
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
required: ["name"],
|
|
29
|
+
properties: {
|
|
30
|
+
name: {
|
|
31
|
+
type: "string",
|
|
32
|
+
minLength: 1,
|
|
33
|
+
description: "The name of the build task type."
|
|
34
|
+
},
|
|
35
|
+
description: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "The description of the build task type."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
databaseType: {
|
|
43
|
+
type: "object",
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
required: ["name"],
|
|
46
|
+
properties: {
|
|
47
|
+
name: {
|
|
48
|
+
type: "string",
|
|
49
|
+
minLength: 1,
|
|
50
|
+
description: "The name of the database type."
|
|
51
|
+
},
|
|
52
|
+
description: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "The description of the database type."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
8
61
|
// build specific schema
|
|
9
62
|
build: {
|
|
10
63
|
type: "object",
|
|
@@ -115,4 +168,4 @@ module.exports = {
|
|
|
115
168
|
]
|
|
116
169
|
}
|
|
117
170
|
}
|
|
118
|
-
}
|
|
171
|
+
}
|
|
@@ -319,6 +319,12 @@ module.exports = class ParserContext {
|
|
|
319
319
|
*/
|
|
320
320
|
getPropertyObject(propertyContent) {
|
|
321
321
|
const property = {};
|
|
322
|
+
if ('$ref' in propertyContent) {
|
|
323
|
+
const refPathParts = propertyContent['$ref'].split('/');
|
|
324
|
+
const propertyName = refPathParts[refPathParts.length - 1];
|
|
325
|
+
property['type'] = { 'ref': [refPathParts[3], propertyName] };
|
|
326
|
+
return property;
|
|
327
|
+
}
|
|
322
328
|
const inputType = this.getTypeFromArray(propertyContent.type);
|
|
323
329
|
const cdsType = this.getDataType(inputType, propertyContent.format);
|
|
324
330
|
|
package/lib/init/add.js
CHANGED
|
@@ -3,6 +3,8 @@ const registered = new Map
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
Plugin: require('./plugin'),
|
|
5
5
|
merge: require('./merge').merge, // fluent API
|
|
6
|
+
removeFromYAML: require('./merge').removeFromYAML,
|
|
7
|
+
removeFromYAMLArray: require('./merge').removeFromYAMLArray,
|
|
6
8
|
readProject: require('./projectReader').readProject,
|
|
7
9
|
mvn: {
|
|
8
10
|
add: require('./mvn').add
|