@sap/cds-dk 7.5.1 → 7.6.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/bin/add.js +28 -24
- package/bin/bind.js +59 -32
- package/bin/build.js +1 -1
- package/bin/cds.js +1 -1
- package/bin/env.js +1 -6
- package/bin/import.js +2 -2
- package/bin/init.js +7 -3
- package/bin/logout.js +2 -2
- package/bin/migrate.js +1 -1
- package/bin/version.js +1 -0
- package/bin/watch.js +8 -1
- package/lib/bind/bindingUtil.js +14 -20
- package/lib/bind/cfKeyProvider.js +8 -10
- package/lib/bind/index.js +4 -2
- package/lib/build/buildTaskFactory.js +31 -20
- package/lib/build/constants.js +9 -8
- package/lib/build/provider/featureToggleBuildPlugin.js +1 -7
- package/lib/build/provider/hana/index.js +44 -9
- package/lib/build/provider/internalBuildTaskProvider.js +7 -5
- package/lib/build/provider/java/index.js +0 -6
- package/lib/build/provider/mtx-sidecar/index.js +5 -0
- package/lib/build/provider/nodejs/index.js +32 -47
- package/lib/build/util.js +134 -33
- package/lib/deploy/to-hana/hana.js +47 -77
- package/lib/deploy/to-hana/hdiDeployUtil.js +15 -18
- package/lib/deploy/to-hana/mtaUtil.js +1 -1
- package/lib/env/schema.js +157 -17
- package/lib/env/schemas/cds-rc.js +118 -0
- package/lib/import/common.js +18 -23
- package/lib/import/odata/metadataconverter/MetadataConverter.js +12 -8
- package/lib/import/odata/v4parser.js +67 -12
- package/lib/init/constants.js +2 -4
- package/lib/init/merge.js +2 -1
- package/lib/init/template/application-logging/index.js +1 -2
- package/lib/init/template/approuter/files/mta.yaml.hbs +2 -0
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/values.yaml.hbs +8 -1
- package/lib/init/template/hana/files/undeploy.json +3 -2
- package/lib/init/template/helm/files/chart/templates/NOTES.txt +2 -0
- package/lib/init/template/helm/files/chart/templates/_deployment_helpers.tpl +10 -2
- package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
- package/lib/init/template/helm/files/content-deployment/templates/_helpers.tpl +6 -5
- package/lib/init/template/helm/files/content-deployment/values.yaml +1 -1
- package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
- package/lib/init/template/helm/files/service-instance/templates/_helpers.tpl +6 -5
- package/lib/init/template/helm/files/service-instance/values.yaml +1 -1
- package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
- package/lib/init/template/helm/files/web-application/templates/_api.tpl +6 -5
- package/lib/init/template/helm/files/web-application/values.yaml +1 -1
- package/lib/init/template/helm/index.js +1 -12
- package/lib/init/template/helm-unified-runtime/index.js +3 -2
- package/lib/init/template/html5-repo/files/values.yaml.hbs +9 -0
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -2
- package/lib/init/template/multitenancy/index.js +3 -2
- package/lib/init/template/xsuaa/files/package.json.hbs +1 -2
- package/lib/lint/Linter.js +2 -2
- package/lib/lint/checks.js +0 -51
- package/lib/mtx/activate.js +1 -1
- package/lib/mtx/auth_manager.js +50 -57
- package/lib/mtx/download_legacy.js +1 -1
- package/lib/mtx/extend.js +2 -2
- package/lib/mtx/pull.js +1 -1
- package/lib/mtx/push.js +1 -1
- package/lib/mtx/settings_manager.js +90 -44
- package/lib/mtx/tenant_action.js +1 -1
- package/lib/mtx/util/dependencies.js +1 -1
- package/lib/mtx/util/legacy_ext.js +0 -1
- package/lib/mtx/util/logging.js +10 -8
- package/lib/util/axios.js +6 -19
- package/lib/util/cf.js +15 -10
- package/npm-shrinkwrap.json +104 -48
- package/package.json +1 -10
- package/lib/env/schemas/cds-rc.json +0 -101
- package/lib/init/template/helm-unified-runtime/lib/build.js +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
1
|
const path = require('path')
|
|
3
2
|
const cds = require('../../cds')
|
|
4
3
|
const { DEFAULT_CSN_FILE_NAME, OUTPUT_MODE, OUTPUT_MODE_RESULT } = require('../constants')
|
|
@@ -65,7 +64,7 @@ class FeatureToggleBuildPlugin extends InternalBuildPlugin {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
async _resolveSourcePaths(csn) {
|
|
68
|
-
const regex = new RegExp(
|
|
67
|
+
const regex = new RegExp(this.ftsName + '[/|\\\\](?<ftName>[^/|\\\\]*)')
|
|
69
68
|
let paths = { base: [] }
|
|
70
69
|
|
|
71
70
|
// add ROOT source file paths for the base model
|
|
@@ -77,11 +76,6 @@ class FeatureToggleBuildPlugin extends InternalBuildPlugin {
|
|
|
77
76
|
return acc
|
|
78
77
|
}, [])
|
|
79
78
|
|
|
80
|
-
// keep existing behavior and return paths returned by cds.resolve if features are not supported by this project
|
|
81
|
-
if (!fs.existsSync(path.join(cds.root, this.ftsName))) {
|
|
82
|
-
return paths
|
|
83
|
-
}
|
|
84
|
-
|
|
85
79
|
// add source file paths for the features
|
|
86
80
|
paths.features = csn['$sources'].reduce((acc, file) => {
|
|
87
81
|
const match = file.match(regex)
|
|
@@ -3,9 +3,10 @@ const path = require('path')
|
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
4
|
const to_hdbmigration = require('./2migration')
|
|
5
5
|
const InternalBuildPlugin = require('../internalBuildPlugin')
|
|
6
|
-
const { BuildError, relativePaths, BuildMessage } = require('../../util')
|
|
6
|
+
const { BuildError, relativePaths, BuildMessage, getWorkspaces, hasOptionValue } = require('../../util')
|
|
7
7
|
const { OUTPUT_MODE_NONE, OUTPUT_MODE, CONTENT_PACKAGE_JSON, CONTENT_HDBTABLEDATA, CSV_FILE_DETECTION,
|
|
8
|
-
CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, CONTENT_NODE_MODULES, OUTPUT_MODE_RESULT, CONTINUE_UNRESOLVED_SCHEMA_CHANGES,
|
|
8
|
+
CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, CONTENT_NODE_MODULES, OUTPUT_MODE_RESULT, CONTINUE_UNRESOLVED_SCHEMA_CHANGES,
|
|
9
|
+
CSV_FILE_TARGET, CONTENT_WS } = require('../../constants')
|
|
9
10
|
const { WARNING } = InternalBuildPlugin
|
|
10
11
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize("src/gen")
|
|
11
12
|
const FILE_EXT_CSV = ".csv"
|
|
@@ -37,7 +38,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
37
38
|
if (!model) {
|
|
38
39
|
return
|
|
39
40
|
}
|
|
40
|
-
// the order is important
|
|
41
|
+
// the order of 1 and 2 is important
|
|
41
42
|
// 1. compile
|
|
42
43
|
const hdiPlugins = await this._compileToHana(model)
|
|
43
44
|
|
|
@@ -48,8 +49,12 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
48
49
|
} else {
|
|
49
50
|
await this._copyResourcesExt(src, dest, model)
|
|
50
51
|
}
|
|
52
|
+
// copy native hana artifacts from workspace dependencies
|
|
53
|
+
if (hasOptionValue(this.task.options?.[CONTENT_WS], true)) {
|
|
54
|
+
await this._addWorkspaceContent()
|
|
55
|
+
}
|
|
51
56
|
|
|
52
|
-
//
|
|
57
|
+
// create additional stuff in dest
|
|
53
58
|
await this._writeHdiConfig(hdiPlugins)
|
|
54
59
|
await this._writeHdiNamespace()
|
|
55
60
|
|
|
@@ -113,16 +118,15 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
// 2. staging build: copy files except *.cds, .env, default-env.json, ./node_modules/**
|
|
117
121
|
if (this.isStagingBuild()) {
|
|
118
122
|
let blockList = "\\.cds$|\\.csv$|\\.hdbtabledata$"
|
|
119
123
|
blockList += !this.hasBuildOption(CONTENT_ENV, true) ? "|\\.env($|\\..*$)" : ""
|
|
120
124
|
blockList += !this.hasBuildOption(CONTENT_DEFAULT_ENV_JSON, true) ? "|default-env\\.json$" : ""
|
|
121
125
|
blockList = new RegExp(blockList)
|
|
122
126
|
|
|
127
|
+
// 2. staging build: copy files except *.cds, .env, default-env.json, ./node_modules/**
|
|
123
128
|
await this.copyNativeContent(src, dest, (entry) => {
|
|
124
129
|
if (entry.startsWith(dbSrc)) {
|
|
125
|
-
// entire native content
|
|
126
130
|
return true
|
|
127
131
|
}
|
|
128
132
|
if (fs.statSync(entry).isDirectory()) {
|
|
@@ -378,11 +382,11 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
378
382
|
}
|
|
379
383
|
|
|
380
384
|
async _writePackageJson() {
|
|
381
|
-
const
|
|
382
|
-
const exists = fs.existsSync(
|
|
385
|
+
const pkgJson = path.join(this.task.src, "package.json")
|
|
386
|
+
const exists = fs.existsSync(pkgJson)
|
|
383
387
|
|
|
384
388
|
if (exists) {
|
|
385
|
-
DEBUG?.(`skip create [${relativePaths(cds.root,
|
|
389
|
+
DEBUG?.(`skip create [${relativePaths(cds.root, pkgJson)}], already existing`)
|
|
386
390
|
}
|
|
387
391
|
if (this.isStagingBuild() && !exists) {
|
|
388
392
|
const content = await HanaBuildPlugin._readTemplateAsJson(FILE_NAME_PACKAGE_JSON)
|
|
@@ -532,6 +536,37 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
532
536
|
}
|
|
533
537
|
}
|
|
534
538
|
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Copy native hana content from workspaces.
|
|
542
|
+
*/
|
|
543
|
+
async _addWorkspaceContent() {
|
|
544
|
+
const srcPaths = new Set()
|
|
545
|
+
const workspaces = await getWorkspaces()
|
|
546
|
+
|
|
547
|
+
workspaces.forEach(workspace => {
|
|
548
|
+
const dbSrc = path.join(cds.root, workspace, 'db/src')
|
|
549
|
+
const dbCfg = path.join(cds.root, workspace, 'db/cfg')
|
|
550
|
+
if (fs.existsSync(dbSrc)) {
|
|
551
|
+
srcPaths.add(dbSrc)
|
|
552
|
+
}
|
|
553
|
+
if (fs.existsSync(dbCfg)) {
|
|
554
|
+
srcPaths.add(dbCfg)
|
|
555
|
+
}
|
|
556
|
+
})
|
|
557
|
+
DEBUG?.(`Copying hana native artifacts from folders '${[...srcPaths].map(f => path.relative(cds.root, f)).join(', ')}'`)
|
|
558
|
+
const dest = path.join(this.task.dest, 'src/gen')
|
|
559
|
+
for (const src of srcPaths) {
|
|
560
|
+
await this.copyNativeContent(src, dest, (entry) => {
|
|
561
|
+
if (entry.match(/(\/|\\)gen(\/|\\)?$/)) {
|
|
562
|
+
// skip gen folder content
|
|
563
|
+
return false
|
|
564
|
+
}
|
|
565
|
+
return true
|
|
566
|
+
})
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
535
570
|
static async _readTemplateAsJson(template) {
|
|
536
571
|
const content = await fs.promises.readFile(path.join(__dirname, 'template', template), 'utf-8')
|
|
537
572
|
return JSON.parse(content.toString())
|
|
@@ -5,7 +5,7 @@ const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue, isCla
|
|
|
5
5
|
const BuildTaskProvider = require('../buildTaskProvider')
|
|
6
6
|
const { FILE_EXT_CDS, BUILD_TASK_HANA, BUILD_TASK_FIORI, BUILD_TASK_JAVA, BUILD_TASK_JAVA_CF, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_MTX,
|
|
7
7
|
BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
|
|
8
|
-
IGNORE_DEFAULT_MODELS } = require("../constants")
|
|
8
|
+
IGNORE_DEFAULT_MODELS, CONTENT_WS } = require("../constants")
|
|
9
9
|
const DEBUG = cds.debug('cli|build')
|
|
10
10
|
|
|
11
11
|
class InternalBuildTaskProvider extends BuildTaskProvider {
|
|
@@ -36,7 +36,7 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
36
36
|
// src folder needs to be initialized first
|
|
37
37
|
InternalBuildTaskProvider._setDefaultSrcFolder(task)
|
|
38
38
|
|
|
39
|
-
InternalBuildTaskProvider._setDefaultModel(task)
|
|
39
|
+
await InternalBuildTaskProvider._setDefaultModel(task)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
getPlugin(task) {
|
|
@@ -290,7 +290,8 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
static _setDefaultModel(task) {
|
|
293
|
+
static async _setDefaultModel(task) {
|
|
294
|
+
const ws = hasOptionValue(task.options?.[CONTENT_WS], true)
|
|
294
295
|
let taskModelPaths = task.options?.model
|
|
295
296
|
if (taskModelPaths && !Array.isArray(taskModelPaths)) {
|
|
296
297
|
taskModelPaths = [taskModelPaths]
|
|
@@ -301,10 +302,11 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
301
302
|
let defaultModelPaths = []
|
|
302
303
|
if (taskModelPaths?.length) {
|
|
303
304
|
if (!hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
|
|
304
|
-
defaultModelPaths = getDefaultModelOptions()
|
|
305
|
+
defaultModelPaths = await getDefaultModelOptions(ws)
|
|
306
|
+
defaultModelPaths = defaultModelPaths.filter(p => p.match(allowList))
|
|
305
307
|
}
|
|
306
308
|
} else {
|
|
307
|
-
defaultModelPaths = getDefaultModelOptions()
|
|
309
|
+
defaultModelPaths = await getDefaultModelOptions(ws)
|
|
308
310
|
defaultModelPaths.push(task.src)
|
|
309
311
|
if (hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
|
|
310
312
|
// all default models except the built-in models
|
|
@@ -90,11 +90,5 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
|
|
|
90
90
|
}
|
|
91
91
|
return m
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
_isCompilerV1() {
|
|
95
|
-
const version = cds.compiler.version()
|
|
96
|
-
const match = version.match(/(\d+)\.?(\d*)\.?(\d*)/)
|
|
97
|
-
return match && match[1] === 1
|
|
98
|
-
}
|
|
99
93
|
}
|
|
100
94
|
module.exports = JavaBuildPlugin
|
|
@@ -50,6 +50,11 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
50
50
|
await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
|
|
51
51
|
await this.collectLanguageBundles(model, path.join(destSidecarSrc, getI18nDefaultFolder()))
|
|
52
52
|
await this.copySrvContent(this.task.src, destSidecar, destSidecar)
|
|
53
|
+
|
|
54
|
+
// copy .npmrc from project root, if it exists and no .npmrc exists in the sidecar
|
|
55
|
+
if (!fs.existsSync(path.join(destSidecar, '.npmrc')) && fs.existsSync(path.join(cds.root, '.npmrc'))) {
|
|
56
|
+
await this.copy(path.join(cds.root, '.npmrc')).to(path.join(destSidecar, '.npmrc'))
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
/**
|
|
@@ -3,7 +3,7 @@ const path = require('path')
|
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
4
|
const cmd = require('../../../util/command');
|
|
5
5
|
const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
6
|
-
const { BuildError } = require('../../util')
|
|
6
|
+
const { BuildError, getWorkspaces } = require('../../util')
|
|
7
7
|
const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
|
|
8
8
|
CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX } = require('../../constants')
|
|
9
9
|
const { WARNING } = EdmxBuildPlugin
|
|
@@ -36,27 +36,26 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
36
36
|
}
|
|
37
37
|
// by default model contains all features
|
|
38
38
|
const model = await this.model()
|
|
39
|
-
if (!model) {
|
|
39
|
+
if (!model && !this.context.options['ws']) {
|
|
40
40
|
return
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
if (model) {
|
|
43
|
+
const { dictionary, sources } = await this.compileAll(model, destSrv, destRoot)
|
|
44
|
+
// collect and write language bundles into single i18n.json file
|
|
45
|
+
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
46
|
+
|
|
47
|
+
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
48
|
+
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
|
|
49
|
+
// inferred flavor is required by edmx compiler backend
|
|
50
|
+
// using cds.compile instead of cds.compiler.compileSources ensures that cds.env options are correctly read
|
|
51
|
+
const baseModel = dictionary.base.meta.flavor !== 'inferred' ? await cds.compile(sources.base, super.options(), 'inferred') : dictionary.base
|
|
52
|
+
await this.compileToEdmx(baseModel, path.join(this.destSrv, 'odata', cds.env.odata.version), compileOptions)
|
|
53
|
+
}
|
|
54
54
|
}
|
|
55
|
-
|
|
56
55
|
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
|
|
57
56
|
const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
58
57
|
await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
|
|
59
|
-
if (this.context.options
|
|
58
|
+
if (this.context.options['ws']) {
|
|
60
59
|
await this._addWorkspaceDependencies(destRoot)
|
|
61
60
|
}
|
|
62
61
|
}
|
|
@@ -160,32 +159,18 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
async _addWorkspaceDependencies(dest) {
|
|
163
|
-
const wsRoot =
|
|
162
|
+
const wsRoot = NodejsBuildPlugin._findWorkspaceRoot(cds.root)
|
|
164
163
|
const appPkg = require(path.join(dest, 'package.json'))
|
|
165
164
|
let workspaces
|
|
166
|
-
if (this.task.options
|
|
165
|
+
if (this.task.options.workspaces) {
|
|
167
166
|
workspaces = Array.isArray(this.task.options.workspaces) ? this.task.options.workspaces : [this.task.options.workspaces]
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
workspaces = []
|
|
171
|
-
for (const name in appPkg.dependencies) {
|
|
172
|
-
if (appPkg.dependencies[name] === '*') {
|
|
173
|
-
try {
|
|
174
|
-
const pkg = require.resolve(path.join(name, 'package.json'), { paths: [wsRoot] })
|
|
175
|
-
workspaces.push(path.dirname(path.relative(wsRoot, pkg)))
|
|
176
|
-
} catch(e) {
|
|
177
|
-
if (e.code === 'MODULE_NOT_FOUND') {
|
|
178
|
-
throw new BuildError(`Workspace packaging failed - module '${name}' not found. Make sure that configured npm workspaces are installed.`)
|
|
179
|
-
}
|
|
180
|
-
throw e
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
167
|
+
} else {
|
|
168
|
+
workspaces = await getWorkspaces(true, false, wsRoot);
|
|
184
169
|
}
|
|
185
170
|
if (workspaces.length) {
|
|
186
171
|
let pkgDescriptors
|
|
187
172
|
try {
|
|
188
|
-
pkgDescriptors = await
|
|
173
|
+
pkgDescriptors = await NodejsBuildPlugin._execNpmPack(dest, wsRoot, workspaces)
|
|
189
174
|
} catch (e) {
|
|
190
175
|
DEBUG?.(e)
|
|
191
176
|
throw new BuildError(`Failed to package npm workspace dependencies\n${e.message}`)
|
|
@@ -193,9 +178,9 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
193
178
|
if (pkgDescriptors?.length) {
|
|
194
179
|
let changed
|
|
195
180
|
for (const pkgDescriptor of pkgDescriptors) {
|
|
196
|
-
const {name, filename: fileName} = pkgDescriptor
|
|
197
|
-
const filePath = path.join(dest, fileName)
|
|
198
|
-
if (appPkg.dependencies?.[name]
|
|
181
|
+
const { name, filename: fileName } = pkgDescriptor
|
|
182
|
+
const filePath = path.join(dest, fileName)
|
|
183
|
+
if (appPkg.dependencies?.[name]) {
|
|
199
184
|
appPkg.dependencies[name] = `file:${fileName}`
|
|
200
185
|
this.pushFile(filePath)
|
|
201
186
|
changed |= true
|
|
@@ -210,28 +195,28 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
210
195
|
}
|
|
211
196
|
}
|
|
212
197
|
|
|
213
|
-
_findWorkspaceRoot(dir) {
|
|
198
|
+
static _findWorkspaceRoot(dir) {
|
|
214
199
|
try {
|
|
215
|
-
if(require(path.join(dir, 'package.json')).workspaces) {
|
|
216
|
-
|
|
200
|
+
if (require(path.join(dir, 'package.json')).workspaces) {
|
|
201
|
+
return dir
|
|
217
202
|
}
|
|
218
|
-
} catch(err) {
|
|
203
|
+
} catch (err) {
|
|
219
204
|
// ignore
|
|
220
205
|
}
|
|
221
|
-
if(fs.existsSync(path.join(dir, '.gitmodules')) || fs.existsSync(path.join(dir, 'node_modules'))) {
|
|
206
|
+
if (fs.existsSync(path.join(dir, '.gitmodules')) || fs.existsSync(path.join(dir, 'node_modules'))) {
|
|
222
207
|
return // project root reached
|
|
223
208
|
}
|
|
224
209
|
if (path.dirname(dir) !== dir) {
|
|
225
210
|
return this._findWorkspaceRoot(path.dirname(dir))
|
|
226
211
|
}
|
|
227
212
|
}
|
|
228
|
-
|
|
229
|
-
async _execNpmPack(dest, wsRoot, workspaces) {
|
|
213
|
+
|
|
214
|
+
static async _execNpmPack(dest, wsRoot, workspaces) {
|
|
230
215
|
const args = ['pack', '-ws', '--json', `--pack-destination=${dest}`]
|
|
231
216
|
workspaces.forEach(w => args.push(`-w=${w}`))
|
|
232
217
|
|
|
233
|
-
DEBUG?.(`execute command: npm ${args}`)
|
|
234
|
-
const result = await cmd.spawnCommand(
|
|
218
|
+
DEBUG?.(`execute command: npm ${args.join(', ')}`)
|
|
219
|
+
const result = await cmd.spawnCommand('npm', args, { cwd: wsRoot }, true, !!DEBUG)
|
|
235
220
|
DEBUG?.(result)
|
|
236
221
|
return JSON.parse(result)
|
|
237
222
|
}
|
package/lib/build/util.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('../cds')
|
|
4
|
-
const
|
|
4
|
+
const cp = require('child_process');
|
|
5
|
+
const execAsync = require('util').promisify(cp.exec)
|
|
6
|
+
const IS_WIN = require('os').platform() === 'win32'
|
|
7
|
+
const { SEVERITY_ERROR, FILE_EXT_CDS } = require('./constants');
|
|
8
|
+
const DEBUG = cds.debug('cli|build')
|
|
5
9
|
|
|
6
10
|
function getProperty(src, segments) {
|
|
7
11
|
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
@@ -113,10 +117,26 @@ function resolveRequiredSapModels(modelPaths) {
|
|
|
113
117
|
})
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
function getDefaultModelOptions() {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
async function getDefaultModelOptions(ws = false) {
|
|
121
|
+
const wildcards = cds.env.requires.toggles && cds.env.features.folders ? ['*', cds.env.features.folders] : '*'
|
|
122
|
+
let paths = _pushModelPaths(cds.resolve(wildcards, false))
|
|
123
|
+
|
|
124
|
+
if (ws) {
|
|
125
|
+
const workspaces = await getWorkspaces()
|
|
126
|
+
const env = cds.env
|
|
127
|
+
const root = cds.root
|
|
128
|
+
try {
|
|
129
|
+
workspaces.forEach(workspace => {
|
|
130
|
+
cds.root = path.join(root, workspace)
|
|
131
|
+
cds.env = cds.env.for('cds', cds.root)
|
|
132
|
+
paths = paths.concat(_pushModelPaths(cds.resolve(cds.env.requires.toggles ? wildcards : '*', false)).map(p => path.join(workspace, p)))
|
|
133
|
+
})
|
|
134
|
+
} finally {
|
|
135
|
+
cds.root = root
|
|
136
|
+
cds.env = env
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return paths
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
/**
|
|
@@ -127,30 +147,6 @@ function normalizePath(dir) {
|
|
|
127
147
|
return typeof dir === "string" ? dir.replace(/\\/g, '/').replace(/\/$/, '') : dir
|
|
128
148
|
}
|
|
129
149
|
|
|
130
|
-
function _pushModelPaths(...modelPaths) {
|
|
131
|
-
const model = new Set()
|
|
132
|
-
// may contain nested arrays
|
|
133
|
-
modelPaths = flatten(modelPaths)
|
|
134
|
-
const { roots } = cds.env
|
|
135
|
-
modelPaths.forEach(m => {
|
|
136
|
-
if (m && !model.has(m) && !model.has(m + "/")) {
|
|
137
|
-
// filter root model paths that do not exist
|
|
138
|
-
// other entries are added as is, e.g. reuse model entries
|
|
139
|
-
if (roots.includes(m)) {
|
|
140
|
-
const dir = path.resolve(cds.root, m)
|
|
141
|
-
if (fs.existsSync(dir)) {
|
|
142
|
-
model.add(m)
|
|
143
|
-
} else if (fs.existsSync(dir + FILE_EXT_CDS)) { //might be cds file name, compatibility to old build configs
|
|
144
|
-
model.add(m)
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
model.add(m)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
return [...model].map(m => normalizePath(m))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
150
|
function flatten(modelPaths) {
|
|
155
151
|
return modelPaths.reduce((acc, m) => {
|
|
156
152
|
if (Array.isArray(m)) {
|
|
@@ -180,6 +176,114 @@ function isClassicMtx() {
|
|
|
180
176
|
return /true/i.test(process.env.OLD_MTX) || _hasMtxDependency()
|
|
181
177
|
}
|
|
182
178
|
|
|
179
|
+
async function pathExists(path) {
|
|
180
|
+
return fs.promises.access(path).then(() => true).catch(() => false);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Returns the list of workspaces defined for the current project. A workspace dependency
|
|
185
|
+
* needs to be defined resulting in a corresponding sym-link.
|
|
186
|
+
* @returns the paths are relative to 'cds.root'.
|
|
187
|
+
*/
|
|
188
|
+
async function getWorkspaces(resolve = false, devDependencies = true, wsRoot = cds.root) {
|
|
189
|
+
const workspaces = []
|
|
190
|
+
const pgkJsonWsRoot = require(path.join(wsRoot, 'package.json'))
|
|
191
|
+
let pgkJson = pgkJsonWsRoot
|
|
192
|
+
if (wsRoot !== cds.root) {
|
|
193
|
+
pgkJson = require(path.join(cds.root, 'package.json'))
|
|
194
|
+
}
|
|
195
|
+
if (pgkJsonWsRoot.workspaces) {
|
|
196
|
+
if (resolve) {
|
|
197
|
+
_validateWsDependencies(wsRoot, pgkJson.dependencies, devDependencies ? pgkJson.devDependencies : {})
|
|
198
|
+
}
|
|
199
|
+
// read npm workspaces
|
|
200
|
+
const pkgs = await _execNpmWs(wsRoot)
|
|
201
|
+
pkgs.forEach(pkg => {
|
|
202
|
+
if (!resolve || devDependencies && pgkJson.devDependencies?.[pkg] || pgkJson.dependencies?.[pkg]) {
|
|
203
|
+
const workspace = _getWorkspace(wsRoot, pkg)
|
|
204
|
+
if (workspace) {
|
|
205
|
+
workspaces.push(workspace)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
DEBUG?.(`Found ${workspaces.length} workspaces ${workspaces.join(', ')}`)
|
|
211
|
+
return workspaces
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Read installed npm workspaces using 'npm ls -ws --json'.
|
|
216
|
+
* @returns {Array} list of package paths, or an empty array if either
|
|
217
|
+
* no workspaces are defined or have not been installed.
|
|
218
|
+
*/
|
|
219
|
+
async function _execNpmWs(wsRoot) {
|
|
220
|
+
try {
|
|
221
|
+
const cmdLine = 'npm ls -ws --json'
|
|
222
|
+
DEBUG?.(`execute ${cmdLine}`)
|
|
223
|
+
let result = await execAsync(cmdLine, { shell: IS_WIN, stdio: ['inherit', 'pipe', 'inherit'], cwd: wsRoot })
|
|
224
|
+
// DEBUG?.(result.stdout)
|
|
225
|
+
result = JSON.parse(result.stdout)
|
|
226
|
+
if (result.dependencies) {
|
|
227
|
+
return Object.keys(result.dependencies)
|
|
228
|
+
} else if (result.name) {
|
|
229
|
+
return [result.name]
|
|
230
|
+
}
|
|
231
|
+
} catch (e) {
|
|
232
|
+
e.stderr = e.stderr?.replace(/npm ERR! /g, '')
|
|
233
|
+
throw e
|
|
234
|
+
}
|
|
235
|
+
return []
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function _getWorkspace(wsRoot, pkg) {
|
|
239
|
+
try {
|
|
240
|
+
const pkgPath = require.resolve(path.join(pkg, 'package.json'), { paths: [wsRoot] })
|
|
241
|
+
if (!pkgPath.match(/(\/|\\)node_modules(\/|\\)?/) && pkgPath.startsWith(wsRoot)) {
|
|
242
|
+
return path.relative(wsRoot, path.dirname(pkgPath))
|
|
243
|
+
}
|
|
244
|
+
} catch (e) {
|
|
245
|
+
// ignore - workspace dependencies have already been validated
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function _validateWsDependencies(wsRoot, dependencies, devDependencies) {
|
|
250
|
+
const allDependencies = {...dependencies, ...devDependencies}
|
|
251
|
+
for (const name in allDependencies) {
|
|
252
|
+
if (allDependencies[name] === '*') {
|
|
253
|
+
try {
|
|
254
|
+
require.resolve(path.join(name, 'package.json'), { paths: [wsRoot] })
|
|
255
|
+
} catch (e) {
|
|
256
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
257
|
+
throw new BuildError(`npm packaging failed, module '${name}' not found. Make sure configured npm workspaces are installed.`)
|
|
258
|
+
}
|
|
259
|
+
throw e
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function _pushModelPaths(...modelPaths) {
|
|
266
|
+
const model = new Set()
|
|
267
|
+
// may contain nested arrays
|
|
268
|
+
modelPaths = flatten(modelPaths)
|
|
269
|
+
const { roots } = cds.env
|
|
270
|
+
modelPaths.forEach(m => {
|
|
271
|
+
if (m && !model.has(m) && !model.has(m + "/")) {
|
|
272
|
+
// filter roots and absolute model paths that do not exist
|
|
273
|
+
if (roots.includes(m)) {
|
|
274
|
+
const dir = path.resolve(cds.root, m)
|
|
275
|
+
if (fs.existsSync(dir)) {
|
|
276
|
+
model.add(m)
|
|
277
|
+
} else if (fs.existsSync(dir + FILE_EXT_CDS)) { //might be cds file name, compatibility to old build configs
|
|
278
|
+
model.add(m)
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
model.add(m)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
return [...model].map(m => normalizePath(m))
|
|
286
|
+
}
|
|
183
287
|
|
|
184
288
|
/**
|
|
185
289
|
* Return gnu-style error string for location `loc`:
|
|
@@ -208,10 +312,6 @@ function _locationString(loc) {
|
|
|
208
312
|
: `${loc.file}:${loc.line}.${loc.col}-${loc.endLine}.${loc.endCol}`;
|
|
209
313
|
}
|
|
210
314
|
|
|
211
|
-
async function pathExists(path) {
|
|
212
|
-
return fs.promises.access(path).then(() => true).catch(() => false);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
315
|
/**
|
|
216
316
|
* Class for individual build message.
|
|
217
317
|
*
|
|
@@ -276,6 +376,7 @@ module.exports = {
|
|
|
276
376
|
isClassicMtx,
|
|
277
377
|
normalizePath,
|
|
278
378
|
pathExists,
|
|
379
|
+
getWorkspaces,
|
|
279
380
|
BuildMessage,
|
|
280
381
|
BuildError
|
|
281
382
|
}
|