@sap/cds-dk 9.6.1 → 9.7.1
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 +50 -0
- package/bin/add.js +5 -2
- package/bin/cds.js +10 -25
- package/bin/data-products/import.js +3 -0
- package/bin/debug.js +76 -16
- package/bin/env.js +2 -1
- package/bin/export.js +18 -13
- package/bin/help/export.md +2 -1
- package/bin/import.js +21 -18
- package/bin/init.js +8 -1
- package/bin/minify.js +11 -12
- package/bin/mock.js +17 -9
- package/bin/repl.js +2 -2
- package/bin/up.js +34 -4
- package/bin/version.js +203 -131
- package/lib/build/buildTaskEngine.js +4 -0
- package/lib/build/buildTaskFactory.js +6 -36
- package/lib/build/index.js +10 -10
- package/lib/build/plugin.js +8 -5
- package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +3 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/_helpers.tpl +1 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +8 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +18 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +11 -1
- package/lib/build/plugins.js +8 -16
- package/lib/build/provider/fiori/index.js +3 -3
- package/lib/build/provider/hana/index.js +16 -19
- package/lib/build/provider/internalBuildPlugin.js +5 -4
- package/lib/build/provider/internalBuildTaskProvider.js +26 -88
- package/lib/build/provider/mtx/index.js +0 -8
- package/lib/build/provider/mtx-sidecar/index.js +88 -80
- package/lib/build/provider/nodejs/index.js +3 -8
- package/lib/build/util.js +52 -115
- package/lib/deploy/to-hana/hana.js +117 -109
- package/lib/env/schemas/cds-rc.js +0 -4
- package/lib/import/odata/v4parser.js +80 -56
- package/lib/init/constants.js +5 -2
- package/lib/init/index.js +48 -41
- package/lib/init/merge.js +7 -2
- package/lib/init/projectReader.js +3 -8
- package/lib/init/template/ams/index.js +1 -4
- package/lib/init/template/attachments/files/package.json.hbs +1 -1
- package/lib/init/template/completion/completionSetup.js +1 -1
- package/lib/init/template/data/as-csv.js +4 -1
- package/lib/init/template/data/index.js +1 -1
- package/lib/init/template/extension/index.js +4 -4
- package/lib/init/template/hana/files/package.json.hbs +3 -3
- package/lib/init/template/handler/index.js +1 -1
- package/lib/init/template/helm/index.js +3 -3
- package/lib/init/template/helm-unified-runtime/index.js +5 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -1
- package/lib/init/template/html5-repo/index.js +6 -5
- package/lib/init/template/ias/files/mta.yaml.hbs +3 -3
- package/lib/init/template/initial/files/.vscode/tasks.json +10 -0
- package/lib/init/template/{nodejs/files/README.md → initial/files/readme.md} +2 -3
- package/lib/init/template/initial/index.js +15 -0
- package/lib/init/template/java/index.js +4 -11
- package/lib/init/template/kyma/index.js +21 -20
- package/lib/init/template/lint/index.js +3 -1
- package/lib/init/template/mta/index.js +0 -5
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -2
- package/lib/init/template/multitenancy/files/values.yaml.hbs +30 -0
- package/lib/init/template/nodejs/files/.vscode/tasks.json +1 -14
- package/lib/init/template/nodejs/files/package.json.hbs +2 -4
- package/lib/init/template/nodejs/index.js +8 -12
- package/lib/init/template/sample/index.js +5 -6
- package/lib/init/template/sqlite/files/package.json.hbs +7 -1
- package/lib/init/template/test/index.js +1 -1
- package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +6 -0
- package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds +5 -0
- package/lib/init/template/tiny-sample/index.js +2 -2
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +6 -0
- package/lib/init/template/workzone-standard/index.js +13 -3
- package/lib/init/template/xsuaa/index.js +1 -1
- package/lib/init/validate.js +11 -6
- package/lib/util/checksum.js +2 -2
- package/lib/util/fs.js +109 -53
- package/lib/util/glob.js +2 -1
- package/lib/util/term.js +1 -7
- package/lib/util/write.js +2 -4
- package/lib/watch/watched.js +1 -1
- package/npm-shrinkwrap.json +233 -266
- package/package.json +2 -2
- package/lib/init/template/nodejs/files/.vscode/settings.json +0 -11
- package/lib/init/template/tiny-sample/files/nodejs/db/data/my.bookshop-Books.csv +0 -3
- package/lib/init/template/tiny-sample/files/nodejs/db/schema.cds +0 -7
- package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds.hbs +0 -5
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
1
|
const cds = require('../../../cds')
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const NodejsBuildPlugin = require('../nodejs')
|
|
2
|
+
const { exists, path } = cds.utils
|
|
3
|
+
const { SEVERITY_ERROR: ERROR, FOLDER_GEN, MTX_SIDECAR_DB_VALIDATION, CONTENT_EDMX, FLAVOR_LOCALIZED_EDMX } = require('../../constants')
|
|
6
4
|
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
7
|
-
const { ERROR } = NodejsBuildPlugin
|
|
8
5
|
const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
|
|
9
6
|
const DEBUG = cds.debug('cli|build')
|
|
10
7
|
|
|
11
8
|
const DEFAULT_MAIN_FOLDER = "_main"
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
const [major, minor] = cds.version.split('.').map(Number)
|
|
11
|
+
const COMPAT = major === 9 && minor < 7 // cds10: remove
|
|
12
|
+
|
|
13
|
+
module.exports = class extends require('../nodejs') {
|
|
14
|
+
|
|
14
15
|
get priority() {
|
|
15
|
-
// must run after 'hana' build task
|
|
16
|
-
return super.priority - 10
|
|
16
|
+
return super.priority - 10 // must run after 'hana' build task
|
|
17
17
|
}
|
|
18
18
|
init() {
|
|
19
19
|
super.init()
|
|
@@ -23,43 +23,51 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Builds the
|
|
27
|
-
* -
|
|
28
|
-
* -
|
|
26
|
+
* Builds the MTX sidecar app consisting of:
|
|
27
|
+
* - Node.js app model defined by the required sidecar services
|
|
28
|
+
* - Main app model defined by the build task's model options, including feature models and resources TAR
|
|
29
29
|
*
|
|
30
30
|
* build.target=".": 'dest' -> 'model-provider/gen'
|
|
31
31
|
* build.target="gen": 'dest' -> 'gen/model-provider'
|
|
32
32
|
*/
|
|
33
33
|
async build() {
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
// REVISIT: required to load plugins from mtx/sidecar, not the main app for the sidecar CSN
|
|
35
|
+
const oldRoot = cds.root
|
|
36
|
+
cds.root = this.task.src
|
|
37
|
+
const sidecarEnv = cds.env.for('cds', this.task.src)
|
|
38
|
+
cds.root = oldRoot
|
|
39
|
+
|
|
40
|
+
const [rMain, rNode] = await Promise.allSettled([
|
|
41
|
+
this._buildMainApp(sidecarEnv),
|
|
42
|
+
this._buildSidecarApp(sidecarEnv)
|
|
43
|
+
])
|
|
44
|
+
if (rMain.status === 'rejected') throw rMain.reason
|
|
45
|
+
if (rNode.status === 'rejected') throw rNode.reason
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
|
-
* Builds the
|
|
44
|
-
* @param {
|
|
49
|
+
* Builds the MTX sidecar Node.js app parts.
|
|
50
|
+
* @param {import('../../../cds').env} sidecarEnv `cds.env` based on the MTX sidecar directory.
|
|
45
51
|
*/
|
|
46
|
-
async
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
async _buildSidecarApp(sidecarEnv) {
|
|
53
|
+
const { dest, src } = this.task
|
|
54
|
+
const model = COMPAT ? this._compatCompileSidecarSync(sidecarEnv) : this._compileSidecarSync(sidecarEnv)
|
|
55
|
+
const srv = path.join(dest, sidecarEnv.folders.srv)
|
|
56
|
+
const promises = [
|
|
57
|
+
this.compileToJson(model, path.join(srv, 'csn.json')),
|
|
58
|
+
this.collectLanguageBundles(model, path.join(srv, getI18nDefaultFolder())),
|
|
59
|
+
this.copySrvContent(src, dest, dest)
|
|
60
|
+
]
|
|
54
61
|
// copy .npmrc from project root, if it exists and no .npmrc exists in the sidecar
|
|
55
|
-
if (!
|
|
56
|
-
|
|
62
|
+
if (!exists(path.join(dest, '.npmrc')) && exists('.npmrc')) {
|
|
63
|
+
promises.push(this.copy(path.join(cds.root, '.npmrc')).to(path.join(dest, '.npmrc')))
|
|
57
64
|
}
|
|
65
|
+
await Promise.all(promises)
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
/**
|
|
61
69
|
* Builds the main app parts containing base model CSN with feature CSNs and resources TAR.
|
|
62
|
-
* @param {
|
|
70
|
+
* @param {import('../../../cds').env} sidecarEnv `cds.env` based on the MTX sidecar directory.
|
|
63
71
|
*/
|
|
64
72
|
async _buildMainApp(sidecarEnv) {
|
|
65
73
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
@@ -78,85 +86,85 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
78
86
|
main = DEFAULT_MAIN_FOLDER
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
const
|
|
82
|
-
const destMain = path.join(destRoot, main)
|
|
89
|
+
const destMain = path.join(this.task.dest, main)
|
|
83
90
|
const destMainSrv = path.join(destMain, cds.env.folders.srv)
|
|
84
91
|
const model = await this.model()
|
|
85
92
|
if (!model) {
|
|
86
93
|
return
|
|
87
94
|
}
|
|
95
|
+
|
|
88
96
|
const { dictionary, sources } = await this.compileAll(model, destMainSrv, destMain)
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
|
|
98
|
+
const promises = []
|
|
99
|
+
promises.push(this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain))
|
|
100
|
+
// promises.push(this.collectAllLanguageBundles(dictionary, sources, destMain, destMain))
|
|
91
101
|
|
|
92
102
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
93
|
-
//inferred model expected by cds.compile.to.edmx()
|
|
103
|
+
// inferred model expected by cds.compile.to.edmx()
|
|
94
104
|
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) } // disabled by default
|
|
95
105
|
const baseModel = cds.compiler.compileSources({ 'base.json': dictionary.base }, { messages: this.messages })
|
|
96
|
-
|
|
106
|
+
promises.push(this.compileToEdmx(baseModel, path.join(destMainSrv, 'odata', cds.env.odata.version), compileOptions))
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
// create resources TAR
|
|
100
110
|
// resources are determined based on available database build task, SQLite as fallback
|
|
101
|
-
|
|
111
|
+
promises.push(new ResourcesTarBuilder(this).createTar(destMain, model))
|
|
102
112
|
|
|
103
113
|
// copy package.json and .cdsrc.json from project root
|
|
104
|
-
|
|
114
|
+
const packageJson = path.join(cds.root, 'package.json')
|
|
115
|
+
const cdsrcJson = path.join(cds.root, '.cdsrc.json')
|
|
116
|
+
if (exists('package.json')) {
|
|
117
|
+
promises.push(this.copy(packageJson).to(path.join(destMain, 'package.json')))
|
|
118
|
+
}
|
|
119
|
+
if (exists('.cdsrc.json')) {
|
|
120
|
+
promises.push(this.copy(cdsrcJson).to(path.join(destMain, '.cdsrc.json')))
|
|
121
|
+
}
|
|
122
|
+
return Promise.all(promises)
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
/**
|
|
108
|
-
* Synchronous compilation using the sidecar context.
|
|
109
|
-
* @param {
|
|
110
|
-
* @returns the compiled
|
|
126
|
+
* Synchronous compilation using the MTX sidecar context.
|
|
127
|
+
* @param {import('../../../cds').env} env `cds.env` based on the MTX sidecar directory.
|
|
128
|
+
* @returns the compiled MTX sidecar CSN
|
|
111
129
|
*/
|
|
112
|
-
|
|
130
|
+
_compileSidecarSync(env) {
|
|
131
|
+
const options = { env, root: this.task.src }
|
|
132
|
+
const modelPaths = COMPAT ? cds.resolve('*', false) : cds.resolve('*', { ...options, dry: true })
|
|
133
|
+
const modelFilePaths = COMPAT ? cds.resolve(modelPaths) : cds.resolve(modelPaths, options)
|
|
134
|
+
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
135
|
+
throw new BuildError("No CDS models found in MTX sidecar. Run the 'npm install' command to install up-to-date versions of the required packages.")
|
|
136
|
+
}
|
|
137
|
+
DEBUG?.(`sidecar model: ${relativePaths(cds.root, modelFilePaths).join(", ")}`)
|
|
138
|
+
|
|
139
|
+
// check whether all models belonging to the @sap namespace can be resolved
|
|
140
|
+
const unresolved = resolveRequiredSapModels(modelPaths)
|
|
141
|
+
if (unresolved.length > 0) {
|
|
142
|
+
// log error, but don't fail
|
|
143
|
+
this.pushMessage(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Run the 'npm install' command to install up-to-date versions of the required packages.`, ERROR)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// using synchronous compilation as cds.root and cds.env have been switched to mtx/sidecar
|
|
147
|
+
// using different messages array ensures that cds compilation doesn't fail because of
|
|
148
|
+
// existing compilation error messages that have been downgraded to warnings
|
|
149
|
+
// see cap/issues/issues/16401#issuecomment-7205397
|
|
150
|
+
const messages = []
|
|
151
|
+
const model = cds.load(modelFilePaths, { sync: true, messages })
|
|
152
|
+
messages.forEach(msg => this.messages.push(msg))
|
|
153
|
+
return model
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// cds10: remove
|
|
157
|
+
_compatCompileSidecarSync(sidecarEnv) {
|
|
113
158
|
const env = cds.env
|
|
114
159
|
const root = cds.root
|
|
115
160
|
try {
|
|
116
161
|
cds.root = this.task.src
|
|
117
162
|
cds.env = sidecarEnv
|
|
118
|
-
|
|
119
|
-
const modelFilePaths = cds.resolve(modelPaths)
|
|
120
|
-
|
|
121
|
-
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
122
|
-
throw new BuildError("No CDS models found in MTX sidecar. Run the 'npm install' command to install up-to-date versions of the required packages.")
|
|
123
|
-
}
|
|
124
|
-
DEBUG?.(`sidecar model: ${relativePaths(cds.root, modelFilePaths).join(", ")}`)
|
|
125
|
-
|
|
126
|
-
// check whether all models belonging to the @sap namespace can be resolved
|
|
127
|
-
const unresolved = resolveRequiredSapModels(modelPaths)
|
|
128
|
-
if (unresolved.length > 0) {
|
|
129
|
-
// log error, but don't fail
|
|
130
|
-
this.pushMessage(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Run the 'npm install' command to install up-to-date versions of the required packages.`, ERROR)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// using synchronous compilation as cds.root and cds.env have been switched to mtx/sidecar
|
|
134
|
-
// using different messages array ensures that cds compilation doesn't fail because of
|
|
135
|
-
// existing compilation error messages that have been downgraded to warnings
|
|
136
|
-
// see cap/issues/issues/16401#issuecomment-7205397
|
|
137
|
-
const messages = []
|
|
138
|
-
const model = cds.load(modelFilePaths, { sync: true, messages })
|
|
139
|
-
messages.forEach(msg => this.messages.push(msg))
|
|
140
|
-
|
|
141
|
-
return model
|
|
163
|
+
return this._compileSidecarSync(sidecarEnv)
|
|
142
164
|
} finally {
|
|
143
165
|
// restore project scope
|
|
144
166
|
cds.root = root
|
|
145
167
|
cds.env = env
|
|
146
168
|
}
|
|
147
169
|
}
|
|
148
|
-
|
|
149
|
-
async _copyMainConfigFiles(src, dest) {
|
|
150
|
-
const packageJson = path.join(src, 'package.json')
|
|
151
|
-
const cdsrcJson = path.join(src, '.cdsrc.json')
|
|
152
|
-
const promises = []
|
|
153
|
-
if (fs.existsSync(packageJson)) {
|
|
154
|
-
promises.push(this.copy(packageJson).to(path.join(dest, 'package.json')))
|
|
155
|
-
}
|
|
156
|
-
if (fs.existsSync(cdsrcJson)) {
|
|
157
|
-
promises.push(this.copy(cdsrcJson).to(path.join(dest, '.cdsrc.json')))
|
|
158
|
-
}
|
|
159
|
-
return promises
|
|
160
|
-
}
|
|
161
170
|
}
|
|
162
|
-
module.exports = MtxSidecarBuildPlugin
|
|
@@ -23,9 +23,6 @@ const arrayify = x => Array.isArray(x) ? x : [x]
|
|
|
23
23
|
class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
24
24
|
init() {
|
|
25
25
|
super.init()
|
|
26
|
-
if (this.task.options.compileDest) {
|
|
27
|
-
throw new BuildError("Option not supported - compileDest")
|
|
28
|
-
}
|
|
29
26
|
// fallback if src has been defined as '.'
|
|
30
27
|
this.destSrv = this.isStagingBuild() ? path.resolve(this.task.dest, cds.env.folders.srv) : path.join(this.task.dest, FOLDER_GEN)
|
|
31
28
|
}
|
|
@@ -53,8 +50,8 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
53
50
|
if (model) {
|
|
54
51
|
const { dictionary, sources } = await this.compileAll(model, destSrv, destRoot)
|
|
55
52
|
// collect and write language bundles into single i18n.json file
|
|
56
|
-
await this.collectAllLanguageBundles(dictionary, sources, destRoot, destRoot)
|
|
57
|
-
|
|
53
|
+
// await this.collectAllLanguageBundles(dictionary, sources, destRoot, destRoot)
|
|
54
|
+
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
58
55
|
|
|
59
56
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
60
57
|
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
|
|
@@ -159,7 +156,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
159
156
|
|
|
160
157
|
async _copyNativeContent(srcRoot, srcSrv, destRoot, destSrv) {
|
|
161
158
|
// project/srv/** -> 'gen/srv/srv/**'
|
|
162
|
-
const filesFilter = await this.copySrvContent(srcSrv, destRoot,
|
|
159
|
+
const filesFilter = await this.copySrvContent(srcSrv, destRoot, destSrv)
|
|
163
160
|
|
|
164
161
|
// project/* -> 'gen/srv/*'
|
|
165
162
|
await this.copyProjectRootContent(srcRoot, destRoot, (entry) => !filesFilter.includes(path.basename(entry)))
|
|
@@ -300,8 +297,6 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
300
297
|
static async _execNpmPack(dest, wsRoot, workspaces) {
|
|
301
298
|
const args = ['pack', '-ws', '--json', `--pack-destination=${dest}`]
|
|
302
299
|
workspaces.forEach(w => args.push(`-w=${w}`))
|
|
303
|
-
|
|
304
|
-
DEBUG?.(`execute command: npm ${args.join(', ')}`)
|
|
305
300
|
const result = await cmd.spawnCommand('npm', args, { cwd: wsRoot }, true, !!LOG._debug)
|
|
306
301
|
DEBUG?.(result)
|
|
307
302
|
return JSON.parse(result)
|
package/lib/build/util.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const cp = require('node:child_process')
|
|
2
|
+
const cp = require('node:child_process')
|
|
3
3
|
const fs = require('node:fs')
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
|
|
6
6
|
const cds = require('../cds')
|
|
7
|
+
const { exists } = cds.utils
|
|
7
8
|
const execAsync = require('node:util').promisify(cp.exec)
|
|
8
9
|
const { isRoot } = require('../util/fs')
|
|
9
10
|
|
|
@@ -14,37 +15,12 @@ const DEBUG = cds.debug('cli|build')
|
|
|
14
15
|
// failsafe to avoid too deep recursions
|
|
15
16
|
// increased from 2 to 5, adding a config options in the future
|
|
16
17
|
const WS_MAX_DEPTH = 5
|
|
18
|
+
const FLATTEN_MAX_DEPTH = 20 // should suffice, was infinite in earlier versions
|
|
17
19
|
|
|
18
20
|
/** @typedef {{col?: number, endCol?: number, line?: number, endLine?: number, file: string}} SourceLocation */
|
|
19
21
|
/** @typedef {object[] | object} Credentials */
|
|
20
22
|
/** @typedef {(string | NestedStringArray)[]} NestedStringArray */
|
|
21
23
|
|
|
22
|
-
/**
|
|
23
|
-
* @param {object} src - object to get property from
|
|
24
|
-
* @param {string | string[]} segments - property path
|
|
25
|
-
*/
|
|
26
|
-
function getProperty(src, segments) {
|
|
27
|
-
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
28
|
-
return segments.reduce((p, n) => p && p[n], src)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {object} src - object to set property on
|
|
33
|
-
* @param {string | string[]} segments - property path
|
|
34
|
-
* @param {unknown} value - value to set
|
|
35
|
-
*/
|
|
36
|
-
function setProperty(src, segments, value) {
|
|
37
|
-
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
38
|
-
segments.reduce((p, n, idx) => {
|
|
39
|
-
if (segments.length === idx + 1) {
|
|
40
|
-
p[n] = value
|
|
41
|
-
} else if (p[n] === undefined) {
|
|
42
|
-
p[n] = {}
|
|
43
|
-
}
|
|
44
|
-
return p[n]
|
|
45
|
-
}, src)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
24
|
/**
|
|
49
25
|
* Returns whether this project is a java project or not.
|
|
50
26
|
*/
|
|
@@ -92,7 +68,7 @@ function relativePaths(root, qualifiedPaths) {
|
|
|
92
68
|
* Returns a list of fully qualified model names belonging to the '@sap' namespace that cannot be resolved.
|
|
93
69
|
* E.g. the module might NOT have been installed.
|
|
94
70
|
* @param {string[]} modelPaths
|
|
95
|
-
* @returns {string[]}
|
|
71
|
+
* @returns {boolean | string[]}
|
|
96
72
|
*/
|
|
97
73
|
function resolveRequiredSapModels(modelPaths) {
|
|
98
74
|
return Array.isArray(modelPaths) && modelPaths.filter(p => {
|
|
@@ -104,14 +80,35 @@ function resolveRequiredSapModels(modelPaths) {
|
|
|
104
80
|
}
|
|
105
81
|
|
|
106
82
|
/**
|
|
107
|
-
* Returns the default model
|
|
83
|
+
* Returns the default model paths for the given workspace.
|
|
108
84
|
* @param {boolean} ws - whether to include workspace models
|
|
109
|
-
* @
|
|
85
|
+
* @returns {Promise<string[]>} array of model paths
|
|
110
86
|
*/
|
|
111
|
-
async function
|
|
112
|
-
|
|
113
|
-
const wildcards =
|
|
114
|
-
|
|
87
|
+
async function getDefaultModelPaths(ws = false) {
|
|
88
|
+
const { toggles } = cds.env.requires, { folders } = cds.env.features
|
|
89
|
+
const wildcards = toggles && folders ? ['*', folders] : '*'
|
|
90
|
+
if (!cds.resolve.locations) return _compatGetDefaultModelPaths(ws, wildcards)
|
|
91
|
+
|
|
92
|
+
let paths = _filterModelPaths(cds.resolve.locations(wildcards))
|
|
93
|
+
if (ws) {
|
|
94
|
+
const workspaces = (await getWorkspaces())
|
|
95
|
+
.map(ws => ws.workspace)
|
|
96
|
+
workspaces.forEach(workspace => {
|
|
97
|
+
const root = path.join(cds.root, workspace)
|
|
98
|
+
const env = cds.env.for('cds', root)
|
|
99
|
+
const resolved = cds.resolve.locations(env.requires.toggles ? wildcards : '*', { env, root })
|
|
100
|
+
paths.push(..._filterModelPaths(resolved, { root, env })
|
|
101
|
+
.map(p => normalizePath(path.join(workspace, p))))
|
|
102
|
+
})
|
|
103
|
+
// exclude redundant built-in models starting with workspace path, e.g. 'books/@sap/cds-mtxs/srv/bootstrap'
|
|
104
|
+
paths = paths.filter(p => NODEJS_MODEL_EXCLUDE_LIST.includes(p) || !NODEJS_MODEL_EXCLUDE_LIST.some(ex => p.endsWith(ex)))
|
|
105
|
+
}
|
|
106
|
+
return paths
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// cds10: remove
|
|
110
|
+
async function _compatGetDefaultModelPaths(ws, wildcards) {
|
|
111
|
+
let paths = _filterModelPaths(cds.resolve(wildcards, false))
|
|
115
112
|
|
|
116
113
|
if (ws) {
|
|
117
114
|
const workspaces = (await getWorkspaces())
|
|
@@ -122,15 +119,15 @@ async function getDefaultModelOptions(ws = false, filter = undefined) {
|
|
|
122
119
|
workspaces.forEach(workspace => {
|
|
123
120
|
cds.root = path.join(root, workspace)
|
|
124
121
|
cds.env = cds.env.for('cds', cds.root)
|
|
125
|
-
|
|
122
|
+
const fromWorkspace = _filterModelPaths(cds.resolve(cds.env.requires.toggles ? wildcards : '*', false))
|
|
123
|
+
paths.push(...fromWorkspace.map(p => normalizePath(path.join(workspace, p))))
|
|
126
124
|
})
|
|
127
125
|
} finally {
|
|
128
126
|
cds.root = root
|
|
129
127
|
cds.env = env
|
|
130
128
|
}
|
|
131
129
|
// exclude redundant built-in models starting with workspace path, e.g. 'books/@sap/cds-mtxs/srv/bootstrap'
|
|
132
|
-
paths = paths
|
|
133
|
-
.filter(p => typeof(p) === 'string' && (NODEJS_MODEL_EXCLUDE_LIST.includes(p) || !NODEJS_MODEL_EXCLUDE_LIST.some(ex => p.endsWith(ex))))
|
|
130
|
+
paths = paths.filter(p => NODEJS_MODEL_EXCLUDE_LIST.includes(p) || !NODEJS_MODEL_EXCLUDE_LIST.some(ex => p.endsWith(ex)))
|
|
134
131
|
}
|
|
135
132
|
return paths
|
|
136
133
|
}
|
|
@@ -143,7 +140,7 @@ async function getDefaultModelOptions(ws = false, filter = undefined) {
|
|
|
143
140
|
/**
|
|
144
141
|
* @overload
|
|
145
142
|
* @param {unknown} dir
|
|
146
|
-
* @returns {unknown}
|
|
143
|
+
* @returns {unknown}
|
|
147
144
|
*/
|
|
148
145
|
/**
|
|
149
146
|
* For valid paths replace '\\' with '/' and remove trailing '/'. Otherwise return as is
|
|
@@ -154,21 +151,6 @@ function normalizePath(dir) {
|
|
|
154
151
|
return typeof dir === "string" ? dir.replace(/\\/g, '/').replace(/\/$/, '') : dir
|
|
155
152
|
}
|
|
156
153
|
|
|
157
|
-
/**
|
|
158
|
-
* @param {NestedStringArray} modelPaths
|
|
159
|
-
*/
|
|
160
|
-
function flatten(modelPaths) {
|
|
161
|
-
// TODO: I guess this could be modelPaths.flat(Infinity) in the future
|
|
162
|
-
return /** @type{string[]}*/(modelPaths.reduce((/** @type {string[]}*/acc, m) => {
|
|
163
|
-
if (Array.isArray(m)) {
|
|
164
|
-
acc = acc.concat(flatten(m))
|
|
165
|
-
} else if (m) {
|
|
166
|
-
acc.push(m)
|
|
167
|
-
}
|
|
168
|
-
return acc
|
|
169
|
-
}, []))
|
|
170
|
-
}
|
|
171
|
-
|
|
172
154
|
function getI18nDefaultFolder() {
|
|
173
155
|
// keep '_i18n' as preferred folder for compatibility reasons
|
|
174
156
|
// if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
|
|
@@ -176,10 +158,6 @@ function getI18nDefaultFolder() {
|
|
|
176
158
|
return cds.env.i18n.folders?.[0] ?? '_i18n'
|
|
177
159
|
}
|
|
178
160
|
|
|
179
|
-
async function pathExists(path) {
|
|
180
|
-
return fs.promises.access(path).then(() => true).catch(() => false);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
161
|
async function getWorkspacePaths() {
|
|
184
162
|
const wsRoot = findWorkspaceRoot(cds.root)
|
|
185
163
|
if (!wsRoot) {
|
|
@@ -246,15 +224,15 @@ async function getWorkspacesDeclarations (root) {
|
|
|
246
224
|
* needs to be defined resulting in a corresponding symlink.
|
|
247
225
|
* @returns {Promise<Workspace[]>} the paths are relative to 'wsRoot'.
|
|
248
226
|
*/
|
|
249
|
-
async function getWorkspaces(wsRoot, resolve = false, devDependencies = true, visited = new Set()) {
|
|
250
|
-
wsRoot ??= findWorkspaceRoot(
|
|
227
|
+
async function getWorkspaces(wsRoot, resolve = false, devDependencies = true, visited = new Set(), root = cds.root) {
|
|
228
|
+
wsRoot ??= findWorkspaceRoot(root)
|
|
251
229
|
if (!wsRoot) {
|
|
252
230
|
return []
|
|
253
231
|
}
|
|
254
232
|
const pkgJsonWsRoot = require(path.join(wsRoot, 'package.json'))
|
|
255
233
|
let pkgJson = pkgJsonWsRoot
|
|
256
|
-
if (wsRoot !==
|
|
257
|
-
pkgJson = require(path.join(
|
|
234
|
+
if (wsRoot !== root) {
|
|
235
|
+
pkgJson = require(path.join(root, 'package.json'))
|
|
258
236
|
}
|
|
259
237
|
const workspaceDependencies = []
|
|
260
238
|
if (pkgJsonWsRoot.workspaces) {
|
|
@@ -271,11 +249,8 @@ async function getWorkspaces(wsRoot, resolve = false, devDependencies = true, vi
|
|
|
271
249
|
if (subproject && !visited.has(pkg)) {
|
|
272
250
|
visited.add(pkg)
|
|
273
251
|
workspaceDependencies.push(subproject)
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
cds.root = subproject.path
|
|
277
|
-
workspaceDependencies.push(...(await getWorkspaces(wsRoot, resolve, devDependencies, visited)))
|
|
278
|
-
cds.root = cdsr
|
|
252
|
+
// recursively resolve transitive deps with the subproject as root
|
|
253
|
+
workspaceDependencies.push(...(await getWorkspaces(wsRoot, resolve, devDependencies, visited, subproject.path)))
|
|
279
254
|
}
|
|
280
255
|
}
|
|
281
256
|
}
|
|
@@ -398,22 +373,21 @@ function _validateWsDependencies(wsRoot, dependencies, devDependencies) {
|
|
|
398
373
|
|
|
399
374
|
/**
|
|
400
375
|
* Returns a flat array of existing files and folders, trailing '/' or '\\' characters have been removed.
|
|
401
|
-
* @param
|
|
402
|
-
* @
|
|
403
|
-
* @returns
|
|
376
|
+
* @param {NestedStringArray | object | undefined } modelPaths array of files and folders - optionally nested
|
|
377
|
+
* @returns {string[] } flat array of existing files and folders
|
|
404
378
|
*/
|
|
405
|
-
function _filterModelPaths(modelPaths,
|
|
379
|
+
function _filterModelPaths(modelPaths, { env = cds.env, root = cds.root } = {}) {
|
|
406
380
|
const model = /** @type {Set<string>}*/(new Set())
|
|
407
381
|
// may contain nested arrays
|
|
408
|
-
const flatModelPaths =
|
|
409
|
-
const { roots } =
|
|
382
|
+
const flatModelPaths = Array.isArray(modelPaths) ? modelPaths.flat(FLATTEN_MAX_DEPTH) : modelPaths ? [modelPaths] : []
|
|
383
|
+
const { roots } = env
|
|
410
384
|
flatModelPaths.forEach(m => {
|
|
411
385
|
if (m && !model.has(m) && !model.has(m + "/")) {
|
|
412
386
|
// filter roots and absolute model paths that do not exist
|
|
413
387
|
if (roots.includes(m)) {
|
|
414
|
-
const
|
|
415
|
-
//
|
|
416
|
-
if (
|
|
388
|
+
const dirent = path.resolve(root, m)
|
|
389
|
+
// might be cds file name, compatibility to old build configs
|
|
390
|
+
if (exists(dirent) || exists(dirent + FILE_EXT_CDS) || exists(dirent.replaceAll('*', ''))) {
|
|
417
391
|
model.add(m)
|
|
418
392
|
}
|
|
419
393
|
} else {
|
|
@@ -422,41 +396,9 @@ function _filterModelPaths(modelPaths, filter = undefined) {
|
|
|
422
396
|
}
|
|
423
397
|
})
|
|
424
398
|
return [...model]
|
|
425
|
-
.filter(m => filter ? filter(m) : true)
|
|
426
399
|
.map(m => normalizePath(m))
|
|
427
400
|
}
|
|
428
401
|
|
|
429
|
-
/**
|
|
430
|
-
* Returns a set of files found in the given directory and its subdirectories.
|
|
431
|
-
* @param {string} srcDir - the directory to search for files
|
|
432
|
-
* @param {(file: string) => boolean} [filter] - the filter function to apply to the found files
|
|
433
|
-
* @param {string[]} files - the set of files to add the found files to
|
|
434
|
-
* @param {boolean} [ignoreSymlinks=false] - whether to ignore symlinks
|
|
435
|
-
* @returns {string[]} files found in the given directory and its subdirectories
|
|
436
|
-
*/
|
|
437
|
-
function getFiles(srcDir, filter, files = [], ignoreSymlinks = false) {
|
|
438
|
-
function handleResource(/** @type {string} entry */ entry) {
|
|
439
|
-
if (!filter || filter.call(this, entry)) {
|
|
440
|
-
const stats = fs.lstatSync(entry)
|
|
441
|
-
if (cds.utils.isdir(entry)) {
|
|
442
|
-
getFiles(entry, filter, files)
|
|
443
|
-
} else if (stats.isFile() || (!ignoreSymlinks && stats.isSymbolicLink())) {
|
|
444
|
-
files.push(entry)
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
let entries = []
|
|
449
|
-
try {
|
|
450
|
-
entries = fs.readdirSync(srcDir)
|
|
451
|
-
} catch {
|
|
452
|
-
// ignore if not existing
|
|
453
|
-
}
|
|
454
|
-
entries.map(subDirEntry => path.join(srcDir, subDirEntry)).forEach((entry) => {
|
|
455
|
-
handleResource(entry)
|
|
456
|
-
})
|
|
457
|
-
return files
|
|
458
|
-
}
|
|
459
|
-
|
|
460
402
|
/**
|
|
461
403
|
* Return gnu-style error string for location `loc`:
|
|
462
404
|
* - 'File:Line:Col' without `loc.end`
|
|
@@ -494,7 +436,7 @@ class BuildMessage {
|
|
|
494
436
|
* @param {string} message The message text
|
|
495
437
|
* @param {import('./constants').Severity} [severity='Error'] severity - severity of the message
|
|
496
438
|
* @param {SourceLocation | undefined} location Location of the message
|
|
497
|
-
*
|
|
439
|
+
*
|
|
498
440
|
* @memberOf BuildMessage
|
|
499
441
|
*/
|
|
500
442
|
constructor(message, severity = SEVERITY_ERROR, location = undefined) {
|
|
@@ -521,7 +463,7 @@ class BuildError extends Error {
|
|
|
521
463
|
/** @type {string[] | {message: string}[]} */
|
|
522
464
|
messages
|
|
523
465
|
|
|
524
|
-
/**
|
|
466
|
+
/**
|
|
525
467
|
* @param {string} message
|
|
526
468
|
* @param {string | string[]} messages
|
|
527
469
|
*/
|
|
@@ -542,22 +484,17 @@ class BuildError extends Error {
|
|
|
542
484
|
}
|
|
543
485
|
|
|
544
486
|
module.exports = {
|
|
545
|
-
getProperty,
|
|
546
|
-
setProperty,
|
|
547
487
|
hasJavaNature,
|
|
548
488
|
hasOptionValue,
|
|
549
489
|
relativePaths,
|
|
550
490
|
resolveRequiredSapModels,
|
|
551
|
-
|
|
552
|
-
flatten,
|
|
491
|
+
getDefaultModelPaths,
|
|
553
492
|
getI18nDefaultFolder,
|
|
554
493
|
normalizePath,
|
|
555
|
-
pathExists,
|
|
556
494
|
getWorkspacePaths,
|
|
557
495
|
getWorkspaces,
|
|
558
496
|
findWorkspaceRoot,
|
|
559
497
|
filterFeaturePaths,
|
|
560
|
-
getFiles,
|
|
561
498
|
BuildMessage,
|
|
562
499
|
BuildError
|
|
563
500
|
}
|