@sap/cds 6.0.4 → 6.1.2
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/CHANGELOG.md +180 -18
- package/apis/cds.d.ts +11 -7
- package/apis/log.d.ts +124 -0
- package/apis/ql.d.ts +72 -15
- package/apis/services.d.ts +13 -2
- package/bin/build/buildTaskHandler.js +5 -2
- package/bin/build/constants.js +4 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
- package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
- package/bin/build/provider/buildTaskProviderInternal.js +22 -14
- package/bin/build/provider/hana/index.js +12 -9
- package/bin/build/provider/java/index.js +18 -8
- package/bin/build/provider/mtx/index.js +7 -4
- package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
- package/bin/build/provider/mtx-extension/index.js +57 -0
- package/bin/build/provider/mtx-sidecar/index.js +46 -18
- package/bin/build/provider/nodejs/index.js +34 -13
- package/bin/deploy/to-hana/cfUtil.js +7 -2
- package/bin/deploy/to-hana/hana.js +20 -25
- package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
- package/bin/serve.js +7 -4
- package/lib/compile/{index.js → cds-compile.js} +0 -0
- package/lib/compile/extend.js +15 -5
- package/lib/compile/minify.js +1 -15
- package/lib/compile/parse.js +1 -1
- package/lib/compile/resolve.js +2 -2
- package/lib/compile/to/srvinfo.js +6 -4
- package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
- package/lib/env/{index.js → cds-env.js} +1 -17
- package/lib/env/{requires.js → cds-requires.js} +24 -3
- package/lib/env/defaults.js +7 -1
- package/lib/env/schemas/cds-package.json +11 -0
- package/lib/env/schemas/cds-rc.json +614 -0
- package/lib/index.js +19 -16
- package/lib/log/{errors.js → cds-error.js} +1 -1
- package/lib/log/{index.js → cds-log.js} +0 -0
- package/lib/log/format/kibana.js +19 -1
- package/lib/ql/Query.js +9 -3
- package/lib/ql/SELECT.js +2 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/{index.js → cds-ql.js} +4 -10
- package/lib/req/context.js +49 -17
- package/lib/req/locale.js +5 -1
- package/lib/{serve → srv}/adapters.js +23 -19
- package/lib/{connect → srv}/bindings.js +0 -0
- package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
- package/lib/{serve/index.js → srv/cds-serve.js} +0 -0
- package/lib/{serve → srv}/factory.js +1 -1
- package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
- package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
- package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
- package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
- package/lib/srv/srv-models.js +207 -0
- package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
- package/lib/utils/{tests.js → cds-test.js} +2 -2
- package/lib/utils/cds-utils.js +146 -0
- package/lib/utils/index.js +2 -145
- package/lib/utils/jest.js +43 -0
- package/lib/utils/resources/index.js +15 -25
- package/lib/utils/resources/tar.js +18 -41
- package/libx/_runtime/auth/index.js +14 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +3 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
- package/libx/_runtime/cds-services/util/errors.js +1 -29
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/perf/index.js +10 -15
- package/libx/_runtime/common/utils/binary.js +3 -4
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
- package/libx/_runtime/common/utils/keys.js +14 -6
- package/libx/_runtime/common/utils/resolveView.js +1 -1
- package/libx/_runtime/common/utils/template.js +1 -1
- package/libx/_runtime/db/Service.js +2 -14
- package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
- package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
- package/libx/_runtime/db/generic/input.js +8 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
- package/libx/_runtime/extensibility/activate.js +47 -47
- package/libx/_runtime/extensibility/add.js +22 -13
- package/libx/_runtime/extensibility/addExtension.js +17 -13
- package/libx/_runtime/extensibility/defaults.js +25 -30
- package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
- package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
- package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
- package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
- package/libx/_runtime/extensibility/linter.js +32 -0
- package/libx/_runtime/extensibility/push.js +77 -20
- package/libx/_runtime/extensibility/service.js +29 -12
- package/libx/_runtime/extensibility/token.js +57 -0
- package/libx/_runtime/extensibility/utils.js +8 -6
- package/libx/_runtime/extensibility/validation.js +6 -9
- package/libx/_runtime/fiori/generic/new.js +0 -11
- package/libx/_runtime/fiori/utils/where.js +1 -1
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
- package/libx/_runtime/hana/pool.js +6 -10
- package/libx/_runtime/hana/search2Contains.js +0 -5
- package/libx/_runtime/hana/search2cqn4sql.js +1 -0
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +11 -6
- package/libx/_runtime/remote/utils/data.js +5 -0
- package/libx/_runtime/sqlite/Service.js +7 -6
- package/libx/_runtime/sqlite/execute.js +41 -28
- package/libx/odata/afterburner.js +79 -2
- package/libx/odata/cqn2odata.js +15 -9
- package/libx/odata/grammar.pegjs +157 -76
- package/libx/odata/index.js +9 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +39 -5
- package/libx/rest/RestAdapter.js +3 -7
- package/libx/rest/middleware/delete.js +4 -5
- package/libx/rest/middleware/parse.js +3 -2
- package/package.json +3 -3
- package/server.js +1 -1
- package/srv/extensibility-service.cds +6 -3
- package/srv/model-provider.cds +3 -1
- package/srv/model-provider.js +86 -106
- package/srv/mtx.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
|
|
3
|
+
const { FOLDER_GEN } = require('../../constants')
|
|
4
|
+
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
5
|
+
|
|
6
|
+
class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
7
|
+
init() {
|
|
8
|
+
super.init()
|
|
9
|
+
if (this.buildOptions.root === this.buildOptions.target) {
|
|
10
|
+
this.task.dest = path.join(this.task.dest, FOLDER_GEN)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async build() {
|
|
15
|
+
const model = await this.model()
|
|
16
|
+
if (!model) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
const allFiles = []
|
|
20
|
+
const destExt = path.join(this.task.dest, 'ext')
|
|
21
|
+
|
|
22
|
+
const packageJson = path.join(destExt, 'package.json')
|
|
23
|
+
await this.copy(path.join(this.task.src, 'package.json')).to(packageJson)
|
|
24
|
+
allFiles.push(packageJson)
|
|
25
|
+
|
|
26
|
+
// extension CSN using parsed format
|
|
27
|
+
const options = { ...this.options(), flavor: 'parsed' }
|
|
28
|
+
const extCsn = await this.cds.load(this.resolveModel(), options)
|
|
29
|
+
if (extCsn.requires) {
|
|
30
|
+
extCsn.requires.length = 0
|
|
31
|
+
}
|
|
32
|
+
const csnFile = path.join(destExt, 'extension.csn')
|
|
33
|
+
await this.compileToJson(extCsn, csnFile)
|
|
34
|
+
allFiles.push(csnFile)
|
|
35
|
+
|
|
36
|
+
const i18n = await this.collectLanguageBundles(extCsn, destExt)
|
|
37
|
+
if (i18n) {
|
|
38
|
+
allFiles.push(i18n.file)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const files = Object.keys(await this.cds.deploy.resources(model))
|
|
42
|
+
if (files.length > 0) {
|
|
43
|
+
const dataDest = path.join(destExt, 'data')
|
|
44
|
+
await Promise.all(
|
|
45
|
+
files
|
|
46
|
+
.filter(file => /\.csv$/.test(file))
|
|
47
|
+
.map(csv => {
|
|
48
|
+
const csvFile = path.join(dataDest, path.basename(csv))
|
|
49
|
+
allFiles.push(csvFile)
|
|
50
|
+
return this.copy(csv).to(csvFile)
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
await new ResourcesTarBuilder(this).writeTarFile(allFiles, destExt, path.join(this.task.dest, 'extension.tgz'))
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
module.exports = MtxExtensionModuleBuilder
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
/* eslint-disable no-empty */
|
|
2
1
|
const path = require('path')
|
|
3
|
-
const
|
|
4
|
-
const
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
5
4
|
const NodeCfModuleBuilder = require('../nodejs')
|
|
6
5
|
const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
|
|
7
|
-
const { INFO } =
|
|
8
|
-
const { relativePaths } = require('
|
|
6
|
+
const { INFO, ERROR } = NodeCfModuleBuilder
|
|
7
|
+
const { relativePaths, BuildError } = require('../../util')
|
|
9
8
|
|
|
10
9
|
const DEFAULT_MAIN_FOLDER = "_main"
|
|
11
10
|
|
|
12
11
|
class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
13
12
|
get priority() {
|
|
14
13
|
// should be scheduled after 'hana' build tasks are finished
|
|
15
|
-
return
|
|
14
|
+
return NodeCfModuleBuilder.PRIORITY_MIN_VALUE
|
|
16
15
|
}
|
|
17
16
|
init() {
|
|
18
17
|
super.init()
|
|
@@ -25,7 +24,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
25
24
|
* Builds the mtx sidecar app consisting of:
|
|
26
25
|
* - nodejs app model defined by the required sidecar services
|
|
27
26
|
* - main app model defined by the build task's model options including feature models and resources TAR
|
|
28
|
-
*
|
|
27
|
+
*
|
|
29
28
|
* build.target=".": 'dest' -> 'model-provider/gen'
|
|
30
29
|
* build.target="gen": 'dest' -> 'gen/model-provider'
|
|
31
30
|
*/
|
|
@@ -46,7 +45,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
46
45
|
const destSidecar = this.task.dest
|
|
47
46
|
const destSidecarSrc = path.join(destSidecar, this.env.folders.srv)
|
|
48
47
|
const model = this._compileSidecarSync(sidecarEnv)
|
|
49
|
-
await this.compileToJson(model, destSidecarSrc)
|
|
48
|
+
await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
|
|
50
49
|
await this.collectLanguageBundles(model, destSidecarSrc)
|
|
51
50
|
await this._copyProjectRootContent(this.task.src, destSidecar)
|
|
52
51
|
}
|
|
@@ -56,11 +55,12 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
56
55
|
* @param {object} sidecarEnv cds env based on the sidecar dir
|
|
57
56
|
*/
|
|
58
57
|
async _buildMainApp(sidecarEnv) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new Error("Invalid sidecar configuration - {\"model-provider\": \"in-sidecar\"} configuration missing")
|
|
58
|
+
if (sidecarEnv.requires['cds.xt.ModelProviderService']?.kind !== 'in-sidecar') {
|
|
59
|
+
throw new Error("Invalid MTX sidecar configuration - \"cds.xt.ModelProviderService\": \"in-sidecar\" missing")
|
|
62
60
|
}
|
|
61
|
+
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
63
62
|
const profiles = this.env.get("profiles") || []
|
|
63
|
+
|
|
64
64
|
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
65
65
|
main = DEFAULT_MAIN_FOLDER
|
|
66
66
|
// root should represent the production use case and not development
|
|
@@ -78,7 +78,10 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
78
78
|
|
|
79
79
|
// create resources TAR
|
|
80
80
|
// resources are determined based on available database build task, SQLite as fallback
|
|
81
|
-
await new ResourcesTarProvider(this).
|
|
81
|
+
await new ResourcesTarProvider(this).createTar(destMain, csn)
|
|
82
|
+
|
|
83
|
+
// copy package.json and .cdsrc.json from project root
|
|
84
|
+
await this._copyMainConfigFiles(this.cds.root, destMain)
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
/**
|
|
@@ -91,20 +94,45 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
91
94
|
try {
|
|
92
95
|
this.cds.root = this.task.src
|
|
93
96
|
this.cds.env = sidecarEnv
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
const modelPaths = this.cds.resolve('*', false)
|
|
98
|
+
const modelFilePaths = this.cds.resolve(modelPaths)
|
|
99
|
+
|
|
100
|
+
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
101
|
+
throw new BuildError("No model found for MTX sidecar app")
|
|
98
102
|
}
|
|
99
|
-
|
|
103
|
+
// candidate for strict mode support
|
|
104
|
+
// entries for @sap/** model paths are missing if the module hasn't been installed, e.g. @sap/cds-mtxs
|
|
105
|
+
const missingModels = modelPaths.filter(p => {
|
|
106
|
+
if (p.startsWith('@sap/')) {
|
|
107
|
+
const files = this.cds.resolve(p)
|
|
108
|
+
return !files || files.length === 0
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
if (missingModels.length > 0) {
|
|
112
|
+
this.pushMessage(`Some model paths could not be resolved. Make sure the npm modules have been installed: ${missingModels.join(', ')}`, ERROR)
|
|
113
|
+
}
|
|
114
|
+
this._logger._debug && this._logger.debug(`sidecar model: ${relativePaths(this.buildOptions.root, modelFilePaths).join(", ")}`)
|
|
100
115
|
|
|
101
116
|
// synchronous compilation
|
|
102
|
-
return this.cds.load(
|
|
117
|
+
return this.cds.load(modelFilePaths, { sync: true, ...this.options() })
|
|
103
118
|
} finally {
|
|
104
119
|
// restore project scope
|
|
105
120
|
this.cds.root = this.buildOptions.root
|
|
106
121
|
this.cds.env = env
|
|
107
122
|
}
|
|
108
123
|
}
|
|
124
|
+
|
|
125
|
+
async _copyMainConfigFiles(src, dest) {
|
|
126
|
+
const packageJson = path.join(src, 'package.json')
|
|
127
|
+
const cdsrcJson = path.join(src, '.cdsrc.json')
|
|
128
|
+
const promises = []
|
|
129
|
+
if (fs.existsSync(packageJson)) {
|
|
130
|
+
promises.push(this.copy(packageJson).to(path.join(dest, 'package.json')))
|
|
131
|
+
}
|
|
132
|
+
if (fs.existsSync(cdsrcJson)) {
|
|
133
|
+
promises.push(this.copy(cdsrcJson).to(path.join(dest, '.cdsrc.json')))
|
|
134
|
+
}
|
|
135
|
+
return promises
|
|
136
|
+
}
|
|
109
137
|
}
|
|
110
138
|
module.exports = MtxSidecarModuleBuilder
|
|
@@ -3,10 +3,10 @@ const path = require('path')
|
|
|
3
3
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
4
4
|
const { BuildError } = require('../../util')
|
|
5
5
|
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION, ODATA_VERSION_V2, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
|
|
6
|
-
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON } = require('../../constants')
|
|
7
|
-
const { WARNING } =
|
|
6
|
+
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, SEMVER_REGEX } = require('../../constants')
|
|
7
|
+
const { WARNING, ERROR } = BuildTaskHandlerEdmx
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
10
10
|
init() {
|
|
11
11
|
super.init()
|
|
12
12
|
// set unified option values in order to ease access later on
|
|
@@ -28,6 +28,12 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
28
28
|
this.destSrv = this.isStagingBuild() ? path.resolve(this.task.dest, this.env.folders.srv) : path.join(this.task.dest, FOLDER_GEN)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
options() {
|
|
32
|
+
const options = super.options(), {cds} = this
|
|
33
|
+
if (cds.requires.extensibility || cds.requires.toggles) options.flavor = 'xtended'
|
|
34
|
+
return options
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
async build() {
|
|
32
38
|
const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, this.env.folders.srv)
|
|
33
39
|
const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
|
|
@@ -48,7 +54,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
48
54
|
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
49
55
|
|
|
50
56
|
if (this.hasBuildOption(CONTENT_EDMX, true)) {
|
|
51
|
-
await this.
|
|
57
|
+
const m = await this.cds.load(sources.base, super.options()) // REVISIT: Quick hack to get inferred model as expected by cds.compile.to.edmx()
|
|
58
|
+
await this.compileToEdmx(m, this.destSrv)
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
@@ -77,11 +84,11 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
|
-
* Copy files from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
|
|
87
|
+
* Copy files from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
|
|
81
88
|
* or <em>destSrv</em> (e.g. 'project/gen/srv/srv') folders according to the file semantics.
|
|
82
|
-
* Files with project semantics like 'package.json' or '.npmrc' file are copied to <em>destRoot</em> while others like '.js' service handlers
|
|
89
|
+
* Files with project semantics like 'package.json' or '.npmrc' file are copied to <em>destRoot</em> while others like '.js' service handlers
|
|
83
90
|
* are copied to <em>destSrv</em>.
|
|
84
|
-
* @param {*} src
|
|
91
|
+
* @param {*} src
|
|
85
92
|
* @param {*} destRoot - folder name representing the app root folder (e.g. gen/srv)
|
|
86
93
|
* @param {*} destSrv - folder name representing the app sub-folder (e.g. gen/srv/srv)
|
|
87
94
|
* @returns the list of files that have been copied
|
|
@@ -98,7 +105,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
98
105
|
// TODO shall not copy language bundles - return !/(\/|\\)(node_modules|_i18n)(\/|\\)?$/.test(entry)
|
|
99
106
|
return !/(\/|\\)node_modules(\/|\\)?$/.test(entry)
|
|
100
107
|
}
|
|
101
|
-
|
|
108
|
+
// make sure the file exists on srv root level - see https://github.tools.sap/cap/issues/issues/12077
|
|
109
|
+
if (srvRootBlockList.test(entry) && path.dirname(entry) === src) {
|
|
102
110
|
srvRootFileNames.push(path.basename(entry))
|
|
103
111
|
return false
|
|
104
112
|
}
|
|
@@ -125,8 +133,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
125
133
|
/**
|
|
126
134
|
* Copy dedicated files (files with project semantics like package.json, .npmrc, .cdsrc, etc.)
|
|
127
135
|
* from the given <em>src</em> folder (e.g. 'project') into the given <em>dest</em> folder (e.g. 'project/gen/srv')
|
|
128
|
-
* @param {*} src
|
|
129
|
-
* @param {*} dest
|
|
136
|
+
* @param {*} src
|
|
137
|
+
* @param {*} dest
|
|
130
138
|
* @param {*} filter - copy file if filter function returns true
|
|
131
139
|
*/
|
|
132
140
|
async _copyProjectRootContent(src, dest, filter) {
|
|
@@ -163,12 +171,18 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
163
171
|
async _modifyPackageJson(file) {
|
|
164
172
|
function _addEnginesField(content) {
|
|
165
173
|
if (!content.engines || !content.engines.node) {
|
|
174
|
+
// use minimum node version of @sap/cds
|
|
166
175
|
const { engines } = require('../../../../package.json')
|
|
167
176
|
if (engines && engines.node) {
|
|
168
177
|
this.logger.log(`${this.task.for}: adding node engines version to package.json ${engines.node}`)
|
|
169
178
|
content.engines = content.engines || {}
|
|
170
|
-
|
|
171
|
-
|
|
179
|
+
const nodeVersion = NodejsModuleBuilder._convertToClosedVersionRange(engines.node)
|
|
180
|
+
if (nodeVersion) {
|
|
181
|
+
content.engines.node = nodeVersion
|
|
182
|
+
return true
|
|
183
|
+
} else {
|
|
184
|
+
this.pushMessage(`Invalid node version defined in engines definition for @sap/cds ${engines.node} - skip node engines definition`, ERROR)
|
|
185
|
+
}
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
return false
|
|
@@ -184,6 +198,13 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
184
198
|
await this.write(content).to(file)
|
|
185
199
|
}
|
|
186
200
|
}
|
|
201
|
+
|
|
202
|
+
static _convertToClosedVersionRange(rtVersion) {
|
|
203
|
+
rtVersion = rtVersion.trim()
|
|
204
|
+
if (rtVersion.match(SEMVER_REGEX)) {
|
|
205
|
+
return rtVersion.replace(SEMVER_REGEX, '^$2$3$4')
|
|
206
|
+
}
|
|
207
|
+
}
|
|
187
208
|
}
|
|
188
209
|
|
|
189
|
-
module.exports =
|
|
210
|
+
module.exports = NodejsModuleBuilder
|
|
@@ -77,7 +77,12 @@ class CfUtil {
|
|
|
77
77
|
async _cfRequest(urlPath, queryObj, bodyObj) {
|
|
78
78
|
if (queryObj) {
|
|
79
79
|
const entries = Object.entries(queryObj);
|
|
80
|
-
const queryStr = entries.map((
|
|
80
|
+
const queryStr = entries.map(([key, value]) => {
|
|
81
|
+
// commas cause problems in cf curl when not double encoded
|
|
82
|
+
value = value.replace(/,/g, encodeURIComponent(','));
|
|
83
|
+
return `${key}=${encodeURIComponent(value)}`;
|
|
84
|
+
}).join('&');
|
|
85
|
+
|
|
81
86
|
urlPath = urlPath + `?${queryStr}`;
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -125,7 +130,7 @@ class CfUtil {
|
|
|
125
130
|
async getCfTargetFromCli() {
|
|
126
131
|
const result = await this._cfRun('target');
|
|
127
132
|
return {
|
|
128
|
-
apiEndpoint: this._extract(result, /api endpoint\s*:\s*(
|
|
133
|
+
apiEndpoint: this._extract(result, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
|
|
129
134
|
user: this._extract(result, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
|
|
130
135
|
org: this._extract(result, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
|
|
131
136
|
space: this._extract(result, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
|
|
@@ -35,7 +35,7 @@ class HanaDeployer {
|
|
|
35
35
|
this.logger = logger;
|
|
36
36
|
|
|
37
37
|
this.logger.log(`${bold('Starting deploy to SAP HANA ...')}`);
|
|
38
|
-
if (vcapFile)
|
|
38
|
+
if (vcapFile) {
|
|
39
39
|
this.logger.log();
|
|
40
40
|
this.logger.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
|
|
41
41
|
bindCallback = null // credentials are given - no cds bind then
|
|
@@ -49,9 +49,9 @@ class HanaDeployer {
|
|
|
49
49
|
|
|
50
50
|
const { buildResults } = await this._build(buildTaskOptions, model);
|
|
51
51
|
|
|
52
|
-
let
|
|
52
|
+
let vcapFileEnv;
|
|
53
53
|
if (vcapFile) {
|
|
54
|
-
|
|
54
|
+
vcapFileEnv = await this._loadDefaultEnv(vcapFile);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
for (const buildResult of buildResults) {
|
|
@@ -71,7 +71,7 @@ class HanaDeployer {
|
|
|
71
71
|
serviceKeyName = cfServiceInstanceKeyName;
|
|
72
72
|
serviceName = serviceName || cfServiceInstanceName;
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
vcapFileEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
75
75
|
|
|
76
76
|
if (!noSave && !bindCallback) {
|
|
77
77
|
await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
|
|
@@ -95,7 +95,7 @@ class HanaDeployer {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
await hdiDeployUtil.deploy(currentModelFolder,
|
|
98
|
+
await hdiDeployUtil.deploy(currentModelFolder, vcapFileEnv, hdiOptions);
|
|
99
99
|
|
|
100
100
|
if (bindCallback) {
|
|
101
101
|
const args = [path.relative(projectPath, buildResult.task.src)];
|
|
@@ -204,16 +204,12 @@ class HanaDeployer {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
|
|
207
|
-
async
|
|
207
|
+
async _loadDefaultEnv(defaultEnvFile) {
|
|
208
208
|
try {
|
|
209
|
-
const content =
|
|
210
|
-
|
|
211
|
-
throw new Error(`The vcap file ${vcapFile} does not contain a VCAP_SERVICES entry.`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return content.VCAP_SERVICES;
|
|
209
|
+
const content = await fs.readFile(defaultEnvFile);
|
|
210
|
+
return JSON.parse(content);
|
|
215
211
|
} catch (err) {
|
|
216
|
-
throw new Error(`Error reading
|
|
212
|
+
throw new Error(`Error reading default env file: ${err.message}`);
|
|
217
213
|
}
|
|
218
214
|
}
|
|
219
215
|
|
|
@@ -237,13 +233,10 @@ class HanaDeployer {
|
|
|
237
233
|
}
|
|
238
234
|
|
|
239
235
|
const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
|
|
240
|
-
defaultEnvJson
|
|
241
|
-
...defaultEnvJson.VCAP_SERVICES,
|
|
242
|
-
...hanaEntry
|
|
243
|
-
}
|
|
236
|
+
Object.assign(defaultEnvJson, hanaEntry);
|
|
244
237
|
|
|
245
238
|
this.logger.log(`Writing ${defaultEnvJsonPath}`);
|
|
246
|
-
await fs.mkdir(path.dirname(defaultEnvJsonPath), {recursive: true})
|
|
239
|
+
await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
|
|
247
240
|
await fs.writeFile(defaultEnvJsonPath, JSON.stringify(defaultEnvJson, null, 2));
|
|
248
241
|
}
|
|
249
242
|
}
|
|
@@ -251,13 +244,15 @@ class HanaDeployer {
|
|
|
251
244
|
|
|
252
245
|
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
253
246
|
return {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
247
|
+
VCAP_SERVICES: {
|
|
248
|
+
hana: [
|
|
249
|
+
{
|
|
250
|
+
name: serviceInstanceName,
|
|
251
|
+
tags: ['hana'],
|
|
252
|
+
credentials: serviceKey
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
}
|
|
261
256
|
};
|
|
262
257
|
}
|
|
263
258
|
|
|
@@ -20,7 +20,10 @@ class HdiDeployUtil {
|
|
|
20
20
|
await this._executeDeploy(dbDir, env, logger);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async deploy(dbDir,
|
|
23
|
+
async deploy(dbDir, vcapEnv, options) {
|
|
24
|
+
vcapEnv = vcapEnv || {}; // handles null and undefined
|
|
25
|
+
options = options || {};
|
|
26
|
+
|
|
24
27
|
LOG.log();
|
|
25
28
|
LOG.log(`Deploying to HANA from ${dbDir}`);
|
|
26
29
|
|
|
@@ -34,7 +37,15 @@ class HdiDeployUtil {
|
|
|
34
37
|
deployerEnv = hdiDeployLib.clean_env(deployerEnv);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
if (vcapEnv.VCAP_SERVICES) {
|
|
41
|
+
deployerEnv.VCAP_SERVICES = JSON.stringify(vcapEnv.VCAP_SERVICES);
|
|
42
|
+
}
|
|
43
|
+
if (vcapEnv.SERVICE_REPLACEMENTS) {
|
|
44
|
+
deployerEnv.SERVICE_REPLACEMENTS = JSON.stringify(vcapEnv.SERVICE_REPLACEMENTS);
|
|
45
|
+
}
|
|
46
|
+
if (vcapEnv.TARGET_CONTAINER) {
|
|
47
|
+
deployerEnv.TARGET_CONTAINER = vcapEnv.TARGET_CONTAINER;
|
|
48
|
+
}
|
|
38
49
|
|
|
39
50
|
if (options.autoUndeploy) {
|
|
40
51
|
LOG.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
|
package/bin/serve.js
CHANGED
|
@@ -181,7 +181,7 @@ async function serve (all=[], o={}) { // NOSONAR
|
|
|
181
181
|
log ('loading plugin:', {impl})
|
|
182
182
|
// TODO support ESM plugins. But see cap/cds/pull/1838#issuecomment-1177200 !
|
|
183
183
|
const plugin = require(impl)
|
|
184
|
-
if (plugin.activate) plugin.activate(each)
|
|
184
|
+
if (plugin.activate) await plugin.activate(each)
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// bootstrap server from project-local server.js or from @sap/cds/server.js
|
|
@@ -258,16 +258,19 @@ function _prepare_logging () { // NOSONAR
|
|
|
258
258
|
cds.on ('serving', (srv) => {
|
|
259
259
|
const details = {}
|
|
260
260
|
if (srv.path) details.path = srv.path
|
|
261
|
-
if (srv._source
|
|
261
|
+
if (srv._source && !srv._source.startsWith('@sap'))
|
|
262
|
+
details.impl = local(srv._source)
|
|
262
263
|
log (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
|
|
263
264
|
})
|
|
264
265
|
|
|
266
|
+
cds.on ('served', ()=> console.log())
|
|
267
|
+
|
|
265
268
|
// print info when we are finally on air
|
|
266
269
|
cds.once ('listening', ({url})=>{
|
|
267
|
-
console.log
|
|
270
|
+
console.log()
|
|
268
271
|
log ('server listening on',{url})
|
|
269
272
|
_timer && console.timeEnd (_timer)
|
|
270
|
-
if (process.stdin.isTTY) log (`[ terminate with ^C ]`)
|
|
273
|
+
if (process.stdin.isTTY) log (`[ terminate with ^C ]\n`)
|
|
271
274
|
})
|
|
272
275
|
}
|
|
273
276
|
|
|
File without changes
|
package/lib/compile/extend.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
const compile = require ('./cds-compile')
|
|
1
2
|
const { extend } = require ('../lazy')
|
|
2
|
-
const compile = require ('./index')
|
|
3
3
|
|
|
4
|
-
module.exports = o => o.definitions ? { with(...
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
module.exports = o => o.definitions ? { with(...csns) {
|
|
5
|
+
|
|
6
|
+
const csn=o, merged = { definitions: {}, extensions: [] }
|
|
7
|
+
for (const { definitions, extensions } of csns) {
|
|
8
|
+
if (definitions) Object.assign(merged.definitions, definitions)
|
|
9
|
+
if (extensions) merged.extensions.push(...extensions)
|
|
10
|
+
}
|
|
11
|
+
const extended = compile({
|
|
12
|
+
'base.csn': compile.to.json(csn),
|
|
13
|
+
'ext.csn': compile.to.json(merged)
|
|
14
|
+
})
|
|
15
|
+
extended.$sources = csn.$sources // required to load resources like i18n later on
|
|
16
|
+
return extended
|
|
17
|
+
|
|
8
18
|
}} : extend(o)
|
package/lib/compile/minify.js
CHANGED
|
@@ -35,7 +35,7 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
|
|
|
35
35
|
}
|
|
36
36
|
function _visit (d) {
|
|
37
37
|
if (typeof d === 'string') {
|
|
38
|
-
if (isInReservedNamespace(d)) return
|
|
38
|
+
if (cds.compiler.model.isInReservedNamespace(d)) return
|
|
39
39
|
else d = all[d]
|
|
40
40
|
} else if (d.ref) return d.ref.reduce((p,n) => {
|
|
41
41
|
let d = (p.elements || csn.definitions[p.target].elements)[n.id || n] // > n.id -> view with parameters
|
|
@@ -61,18 +61,4 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
|
|
|
61
61
|
return minified
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/**
|
|
65
|
-
* TODO - keep in sync with @sap/cds-compiler. Use their public API once it is available!
|
|
66
|
-
* Checks whether the given absolute path is inside a reserved namespace.
|
|
67
|
-
*
|
|
68
|
-
* @param {string} absolute
|
|
69
|
-
* @returns {boolean}
|
|
70
|
-
*/
|
|
71
|
-
function isInReservedNamespace(absolute) {
|
|
72
|
-
return absolute.startsWith( 'cds.') &&
|
|
73
|
-
!absolute.match(/^cds\.foundation(\.|$)/) &&
|
|
74
|
-
!absolute.match(/^cds\.outbox(\.|$)/) && // Requested by Node runtime
|
|
75
|
-
!absolute.match(/^cds\.xt(\.|$)/); // Requested by Mtx
|
|
76
|
-
}
|
|
77
|
-
|
|
78
64
|
const _minified = Symbol('minified')
|
package/lib/compile/parse.js
CHANGED
|
@@ -56,7 +56,7 @@ const tagged = (parse, strings, ...values) => {
|
|
|
56
56
|
all[2*i] = s
|
|
57
57
|
all[2*i+1] = v instanceof cds.entity ? v.name : ':'+i
|
|
58
58
|
if (typeof v === 'string' && s.endsWith(' like ') && !v.includes('%')) values[i] = `%${v}%`
|
|
59
|
-
if (Array.isArray(v) && s.endsWith(' in ')) values[i] = {list: v.map(cxn4)}
|
|
59
|
+
if (Array.isArray(v) && s.toLowerCase().endsWith(' in ')) values[i] = {list: v.map(cxn4)}
|
|
60
60
|
}
|
|
61
61
|
all[2*i] = strings[i]
|
|
62
62
|
return merge (parse(all.join('')), values)
|
package/lib/compile/resolve.js
CHANGED
|
@@ -20,7 +20,7 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
|
|
|
20
20
|
if (model === '*') return _resolve_all(o,this)
|
|
21
21
|
if (Array.isArray(model)) {
|
|
22
22
|
const resolved = [... new Set(model)] .reduce ((prev,next) => prev.concat (this.resolve(next,o)||[]), [])
|
|
23
|
-
return o.dry || o === false ? resolved : _resolved (resolved)
|
|
23
|
+
return o.dry || o === false ? [...new Set(resolved.flat())] : _resolved (resolved)
|
|
24
24
|
}
|
|
25
25
|
if (model.endsWith('/*')) return _resolve_subdirs_in(model,o,this)
|
|
26
26
|
|
|
@@ -69,7 +69,7 @@ const _required = (cds,env=cds.env) => Object.values(env.requires) .map (r => r.
|
|
|
69
69
|
const _resolve = require('module')._resolveFilename
|
|
70
70
|
|
|
71
71
|
function _resolve_all (o,cds) {
|
|
72
|
-
const {roots} = o.env || cds.env; if (o.dry || o === false) return [ ...roots, ..._required(cds) ]
|
|
72
|
+
const {roots} = o.env || cds.env; if (o.dry || o === false) return [ ...roots, ...new Set(_required(cds).flat()) ]
|
|
73
73
|
const cache = o.cache || exports.cache
|
|
74
74
|
const cached = cache['*']; if (cached) return cached
|
|
75
75
|
cache['*'] = [] // important to avoid endless recursion on '*'
|
|
@@ -69,21 +69,23 @@ module.exports = (model, options={}) => {
|
|
|
69
69
|
|
|
70
70
|
function _javaPrefix() {
|
|
71
71
|
let is_java
|
|
72
|
+
const javaPrefixDefault = 'odata/v4/'
|
|
72
73
|
for (let s of model.$sources) {
|
|
73
74
|
const file = isfile (join (s,'../src/main/resources/application.yaml'))
|
|
74
75
|
if (file) {
|
|
75
76
|
const yaml = cds.load.yaml(file)
|
|
76
|
-
for (let
|
|
77
|
+
for (let yamlDoc of Array.isArray(yaml) ? yaml : [yaml]) {
|
|
78
|
+
const cds = yamlDoc?.cds;
|
|
77
79
|
if (!cds) continue
|
|
78
80
|
return cds['odataV4.endpoint.path'] || cds['odataV2.endpoint.path'] // https://cap.cloud.sap/docs/java/application-services#configure-base-path
|
|
79
81
|
|| cds['odata-v4.endpoint.path'] || cds['odata-v2.endpoint.path'] // older/intermediate config, keep for backward compatibility
|
|
80
|
-
|
|
82
|
+
|| javaPrefixDefault
|
|
81
83
|
}
|
|
82
|
-
return
|
|
84
|
+
return javaPrefixDefault
|
|
83
85
|
}
|
|
84
86
|
else if (isfile (join(s,'../pom.xml'))) is_java = true
|
|
85
87
|
}
|
|
86
|
-
return is_java &&
|
|
88
|
+
return is_java && javaPrefixDefault
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cds = require('
|
|
1
|
+
const cds = require('../index'), { local, inspect } = cds.utils
|
|
2
2
|
const DEBUG = cds.debug('deploy')
|
|
3
3
|
/* eslint-disable no-console */
|
|
4
4
|
|
|
@@ -111,7 +111,7 @@ exports.create = async function (db, csn=db.model, o) {
|
|
|
111
111
|
console.log(); for (let each of drops) console.log(each)
|
|
112
112
|
console.log(); for (let each of creates) console.log(each,'\n')
|
|
113
113
|
return
|
|
114
|
-
} else return db.
|
|
114
|
+
} else return db.run (async tx => {
|
|
115
115
|
await tx.run(drops)
|
|
116
116
|
await tx.run(creates)
|
|
117
117
|
return true
|
|
@@ -120,8 +120,9 @@ exports.create = async function (db, csn=db.model, o) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
exports.init = (db, csn=db.model, log=()=>{}) => db.
|
|
124
|
-
const resources = await exports.resources(csn
|
|
123
|
+
exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
|
|
124
|
+
const resources = await exports.resources(csn, {testdata: cds.env.features.test_data})
|
|
125
|
+
const inits=[]
|
|
125
126
|
for (let [file,e] of Object.entries(resources)) {
|
|
126
127
|
if (e === '*') { // init.js/ts
|
|
127
128
|
let x = await cds.utils._import(file); if (!x) continue
|
|
@@ -143,9 +144,9 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.tx (async tx => {
|
|
|
143
144
|
})
|
|
144
145
|
|
|
145
146
|
|
|
146
|
-
exports.resources = async function (csn) {
|
|
147
|
+
exports.resources = async function (csn, opts) {
|
|
147
148
|
if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
|
|
148
|
-
const folders = await exports.resources.folders(csn)
|
|
149
|
+
const folders = await exports.resources.folders(csn, opts)
|
|
149
150
|
const found={}, ts = process.env.CDS_TYPESCRIPT
|
|
150
151
|
for (let folder of folders) {
|
|
151
152
|
// fetching init.js files
|
|
@@ -173,11 +174,11 @@ exports.resources = async function (csn) {
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
|
|
176
|
-
exports.resources.folders = async function (csn) {
|
|
177
|
+
exports.resources.folders = async function (csn, o={}) {
|
|
177
178
|
if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
|
|
178
179
|
const folders = new Set (csn.$sources.map (path.dirname) .filter (f => f !== cds.home))
|
|
179
180
|
if (cds.env.folders.db) folders.add (path.resolve(cds.root, cds.env.folders.db))
|
|
180
|
-
if (
|
|
181
|
+
if (o.testdata) folders.add (path.resolve(cds.root,'test/'))
|
|
181
182
|
return folders
|
|
182
183
|
}
|
|
183
184
|
|