@sap/cds-dk 9.6.0 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/bin/add.js +5 -2
- package/bin/cds.js +11 -26
- package/bin/compile/index.js +3 -2
- 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 +202 -131
- package/lib/build/buildTaskEngine.js +4 -0
- package/lib/build/buildTaskFactory.js +5 -35
- package/lib/build/index.js +10 -10
- package/lib/build/plugin.js +6 -3
- 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/hana/index.js +16 -19
- package/lib/build/provider/internalBuildPlugin.js +28 -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 -87
- 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 +44 -41
- package/lib/init/merge.js +7 -2
- package/lib/init/projectReader.js +4 -10
- package/lib/init/template/ams/index.js +1 -4
- package/lib/init/template/attachments/files/package.json.hbs +1 -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/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/files/app/common.cds +1 -1
- 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/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 +4 -8
- package/lib/util/write.js +2 -4
- package/lib/watch/livereload-server.js +4 -3
- package/lib/watch/node-watch.js +5 -8
- package/lib/watch/watched.js +1 -1
- package/lib/watch/watcher.js +136 -0
- package/npm-shrinkwrap.json +245 -284
- package/package.json +2 -3
- 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
|
@@ -108,3 +108,33 @@ sidecar:
|
|
|
108
108
|
path: /health
|
|
109
109
|
readiness:
|
|
110
110
|
path: /health
|
|
111
|
+
|
|
112
|
+
mtx-upgrade:
|
|
113
|
+
bindings:
|
|
114
|
+
{{#isJava}}
|
|
115
|
+
{{#hasXsuaa}}
|
|
116
|
+
saas-registry:
|
|
117
|
+
serviceInstanceName: saas-registry
|
|
118
|
+
{{/hasXsuaa}}
|
|
119
|
+
{{^hasXsuaa}}
|
|
120
|
+
subscription-manager:
|
|
121
|
+
serviceInstanceName: subscription-manager
|
|
122
|
+
{{/hasXsuaa}}
|
|
123
|
+
{{/isJava}}
|
|
124
|
+
service-manager:
|
|
125
|
+
serviceInstanceName: service-manager
|
|
126
|
+
image:
|
|
127
|
+
repository: {{appName}}-sidecar
|
|
128
|
+
resources:
|
|
129
|
+
limits:
|
|
130
|
+
ephemeral-storage: 1G
|
|
131
|
+
memory: 1G
|
|
132
|
+
requests:
|
|
133
|
+
ephemeral-storage: 1G
|
|
134
|
+
cpu: 1000m
|
|
135
|
+
memory: 1G
|
|
136
|
+
command: ["launcher"]
|
|
137
|
+
args:
|
|
138
|
+
- "cds-mtx"
|
|
139
|
+
- "upgrade"
|
|
140
|
+
- '*'
|
|
@@ -4,20 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"type": "shell",
|
|
6
6
|
"label": "cds watch",
|
|
7
|
-
"command": "cds",
|
|
8
|
-
"args": ["watch"],
|
|
9
|
-
"group": {
|
|
10
|
-
"kind": "build",
|
|
11
|
-
"isDefault": true
|
|
12
|
-
},
|
|
13
|
-
"problemMatcher": []
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"type": "shell",
|
|
17
|
-
"label": "cds serve",
|
|
18
|
-
"command": "cds",
|
|
19
|
-
"args": ["serve", "--with-mocks", "--in-memory?"],
|
|
20
|
-
"problemMatcher": []
|
|
7
|
+
"command": "cds watch",
|
|
21
8
|
}
|
|
22
9
|
]
|
|
23
10
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "{{ projectName }}",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "A simple CAP project.",
|
|
5
4
|
"dependencies": {
|
|
6
|
-
"@sap/cds": "^9"
|
|
7
|
-
"express": "^4"
|
|
5
|
+
"@sap/cds": "^9"
|
|
8
6
|
},
|
|
9
7
|
"devDependencies": {
|
|
10
8
|
"@cap-js/sqlite": "^2"
|
|
@@ -13,4 +11,4 @@
|
|
|
13
11
|
"start": "cds-serve"
|
|
14
12
|
},
|
|
15
13
|
"private": true
|
|
16
|
-
}
|
|
14
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
1
|
const cds = require('../../../cds')
|
|
3
2
|
const { exists, mkdirp, path } = cds.utils
|
|
4
|
-
const {
|
|
5
|
-
const { URLS, OPTIONS: { NODEJS, JAVA }, COMMAND_ADD } = require('../../constants')
|
|
3
|
+
const { OPTIONS: { NODEJS, JAVA } } = require('../../constants')
|
|
6
4
|
const term = require('../../../util/term')
|
|
5
|
+
const { merge } = require('../../merge')
|
|
7
6
|
const { renderAndCopy } = require('../../template')
|
|
8
7
|
|
|
9
8
|
module.exports = class NodejsTemplate extends require('../../plugin') {
|
|
@@ -17,9 +16,6 @@ module.exports = class NodejsTemplate extends require('../../plugin') {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
async canRun() {
|
|
20
|
-
if (cds.cli.command === COMMAND_ADD) {
|
|
21
|
-
throw `You can't change the runtime of an existing project.`
|
|
22
|
-
}
|
|
23
19
|
if (cds.cli.options.add?.has(JAVA)) {
|
|
24
20
|
throw `only one runtime per project is supported – specify either ${term.bold(JAVA)} or ${term.bold(NODEJS)}`
|
|
25
21
|
}
|
|
@@ -27,13 +23,13 @@ module.exports = class NodejsTemplate extends require('../../plugin') {
|
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
async run() {
|
|
30
|
-
const { db, srv, app } =
|
|
31
|
-
|
|
26
|
+
const { db, srv, app } = cds.env.folders
|
|
27
|
+
if (cds.cli.command === 'init' && cds.cli.options.force) {
|
|
28
|
+
await renderAndCopy(path.join(__dirname, 'files'), cds.root, { projectName: path.basename(cds.root) })
|
|
29
|
+
} else {
|
|
30
|
+
await merge(__dirname, 'files/package.json.hbs').into('package.json', { with: { projectName: path.basename(cds.root) }})
|
|
31
|
+
}
|
|
32
32
|
await Promise.all([db, srv, app].map(p => mkdirp(p)))
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async finalize() {
|
|
36
|
-
console.log(`\nfind samples on ${term.link(URLS.SAMPLES)}`)
|
|
37
|
-
console.log(`learn about next steps at ${term.link(URLS.CAPIRE)}`)
|
|
38
|
-
}
|
|
39
35
|
}
|
|
@@ -226,7 +226,7 @@ annotate common.Currencies with @(UI : {
|
|
|
226
226
|
//
|
|
227
227
|
// Currencies Elements
|
|
228
228
|
//
|
|
229
|
-
annotate common.Currencies with {
|
|
229
|
+
annotate sap.common.Currencies with {
|
|
230
230
|
numcode @title: '{i18n>NumCode}';
|
|
231
231
|
minor @title: '{i18n>MinorUnit}';
|
|
232
232
|
exponent @title: '{i18n>Exponent}';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { join } = require('path')
|
|
2
|
-
const cds = require('../../../cds')
|
|
3
|
-
const {
|
|
2
|
+
const cds = require('../../../cds')
|
|
3
|
+
const { read, write, copy } = cds.utils
|
|
4
|
+
const { readProject } = require('../../projectReader')
|
|
4
5
|
const { merge } = require('../../merge')
|
|
5
6
|
const { renderAndCopy } = require('../../template')
|
|
6
7
|
const mvn = require('../../mvn')
|
|
@@ -18,10 +19,8 @@ module.exports = class SampleTemplate extends require('../../plugin') {
|
|
|
18
19
|
const ExtensionGenerator = require('../extension')
|
|
19
20
|
return new ExtensionGenerator().addSample()
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const { db, srv, app } = env.folders
|
|
24
|
-
const i18n = env.i18n.folders[0]
|
|
22
|
+
const { db, srv, app } = cds.env.folders
|
|
23
|
+
const i18n = cds.env.i18n.folders[0]
|
|
25
24
|
const { appName, appPath } = project
|
|
26
25
|
|
|
27
26
|
if (project.language === 'java') {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
{{#profileProduction}}
|
|
3
|
+
"dependencies": {
|
|
4
|
+
"@cap-js/sqlite": "^2"
|
|
5
|
+
}
|
|
6
|
+
{{/profileProduction}}
|
|
7
|
+
{{^profileProduction}}
|
|
2
8
|
"devDependencies": {
|
|
3
9
|
"@cap-js/sqlite": "^2"
|
|
4
|
-
}{{#profile}},
|
|
10
|
+
}{{/profileProduction}}{{#profile}},
|
|
5
11
|
"cds": {
|
|
6
12
|
"requires": {
|
|
7
13
|
"[{{profile}}]": {
|
|
@@ -144,7 +144,7 @@ async function createNodeTest(proj) {
|
|
|
144
144
|
*/
|
|
145
145
|
async function loadModel() {
|
|
146
146
|
try {
|
|
147
|
-
return cds.linked(cds.minify(await cds.load(
|
|
147
|
+
return cds.linked(cds.minify(await cds.load(cds.env.roots)))
|
|
148
148
|
} catch (err) {
|
|
149
149
|
if (err.code === 'MODEL_NOT_FOUND') return null
|
|
150
150
|
throw new Error(`Error compiling CDS files. Run 'npm install' and try again.`, {cause:err})
|
|
@@ -2,7 +2,7 @@ const { join } = require('path')
|
|
|
2
2
|
const cds = require('../../../../lib/cds'), { copy } = cds.utils
|
|
3
3
|
const { renderAndCopy } = require('../../template')
|
|
4
4
|
const mvn = require('../../mvn')
|
|
5
|
-
const {
|
|
5
|
+
const { readProject } = require('../../projectReader')
|
|
6
6
|
const { JAVA } = require('../../constants')
|
|
7
7
|
|
|
8
8
|
module.exports = class TinySampleTemplate extends require('../../plugin') {
|
|
@@ -18,7 +18,7 @@ module.exports = class TinySampleTemplate extends require('../../plugin') {
|
|
|
18
18
|
await mvn.add('tiny_sample')
|
|
19
19
|
}
|
|
20
20
|
} else {
|
|
21
|
-
const { db, srv } =
|
|
21
|
+
const { db, srv } = cds.env.folders
|
|
22
22
|
await Promise.all([
|
|
23
23
|
copy(join(__dirname, 'files', 'nodejs', 'db')).to(db),
|
|
24
24
|
renderAndCopy(join(__dirname, 'files', 'nodejs', 'srv'), srv, { dbFolder: db.replace(/[\\/]+$/, '') })
|
|
@@ -3,7 +3,7 @@ const { read, write, exists } = cds.utils
|
|
|
3
3
|
const { readProject } = require('../../projectReader')
|
|
4
4
|
const { merge } = require('../../merge')
|
|
5
5
|
const { join } = require('path')
|
|
6
|
-
const { destination, destinations, srv4, html5RepoHost, mtxSidecar4 } = require('../../registries/mta')
|
|
6
|
+
const { destination, destinations, xsuaa, srv4, html5RepoHost, mtxSidecar4 } = require('../../registries/mta')
|
|
7
7
|
const { odataApi } = require('../../registries/xs-app')
|
|
8
8
|
|
|
9
9
|
module.exports = class extends require('../../plugin') {
|
|
@@ -41,7 +41,7 @@ module.exports = class extends require('../../plugin') {
|
|
|
41
41
|
|
|
42
42
|
async combine() {
|
|
43
43
|
const project = readProject()
|
|
44
|
-
const { isJava, addMta, hasMta, srvPath, apps, appPath, appUIPaths, addMultitenancy, strippedAppName } = project
|
|
44
|
+
const { isJava, addMta, hasMta, srvPath, apps, appPath, appUIPaths, addMultitenancy, addXsuaa, strippedAppName } = project
|
|
45
45
|
|
|
46
46
|
await Promise.all(appUIPaths.map(async p => {
|
|
47
47
|
project.uiApp = p
|
|
@@ -78,7 +78,7 @@ module.exports = class extends require('../../plugin') {
|
|
|
78
78
|
await write('mtx/sidecar/package.json', mtxPackageJson, { spaces: 2 })
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
if (addMta || addMultitenancy && hasMta) {
|
|
81
|
+
if (addMta || (addMultitenancy || addXsuaa) && hasMta) {
|
|
82
82
|
const srv = srv4(srvPath)
|
|
83
83
|
const additions = [srv, destinations, destination, {
|
|
84
84
|
in: [destination, 'requires'],
|
|
@@ -91,6 +91,16 @@ module.exports = class extends require('../../plugin') {
|
|
|
91
91
|
into: [mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar'), 'requires', 'name']
|
|
92
92
|
})
|
|
93
93
|
}
|
|
94
|
+
if (addXsuaa) {
|
|
95
|
+
relationships.push({
|
|
96
|
+
insert: [xsuaa],
|
|
97
|
+
into: [destinations, 'requires']
|
|
98
|
+
})
|
|
99
|
+
additions.push({
|
|
100
|
+
in: [destinations, 'parameters.content.instance.destinations' ],
|
|
101
|
+
where: { Authentication: 'OAuth2UserTokenExchange' }
|
|
102
|
+
})
|
|
103
|
+
}
|
|
94
104
|
|
|
95
105
|
await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { with: project, additions, relationships })
|
|
96
106
|
}
|
package/lib/init/validate.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
const { PROJECT_FILES } = require('./constants')
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// max. 64 chars, starts with letter, number, '_', contains only letters (upper and lower case), numbers, '_' or '-'
|
|
4
|
+
const REGEX_PROJECT_NAME = /^[0-9a-z_][0-9a-z_-]{0,63}$/i
|
|
5
|
+
|
|
3
6
|
const cds = require('../../lib/cds')
|
|
4
7
|
const { exists } = cds.utils
|
|
5
8
|
const { bold } = require('../../lib/util/term')
|
|
6
9
|
|
|
7
10
|
module.exports.projectName = projectName => {
|
|
8
|
-
if (!projectName) throw '
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
if (!projectName) throw 'Specify a project name.'
|
|
12
|
+
if (!projectName.match(REGEX_PROJECT_NAME)) {
|
|
13
|
+
throw `This project name is not supported.
|
|
14
|
+
Specify a different project name which
|
|
15
|
+
starts with a letter, number or '_' and
|
|
16
|
+
contains only letters, numbers, '_' or '-' and
|
|
17
|
+
is 64 characters or less.`
|
|
13
18
|
}
|
|
14
19
|
}
|
|
15
20
|
|
package/lib/util/checksum.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cds = require('../../lib/cds')
|
|
2
2
|
const { read, stat } = cds.utils
|
|
3
3
|
const crypto = require('crypto')
|
|
4
|
-
const {
|
|
4
|
+
const { find } = require('../util/fs')
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Calculate a checksum for a directory, excluding specified patterns.
|
|
@@ -25,7 +25,7 @@ async function calcDirChecksum(dirPath, filter = [], hashFunction = 'sha256', op
|
|
|
25
25
|
return hash.digest('hex')
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const files = [...(await
|
|
28
|
+
const files = [...(await find(dirPath, { filter, ignoreSymlinks: true })), ...optionalFiles].sort()
|
|
29
29
|
for (const file of files) {
|
|
30
30
|
try {
|
|
31
31
|
hash.update(file)
|
package/lib/util/fs.js
CHANGED
|
@@ -1,69 +1,125 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const os = require('node:os')
|
|
2
|
+
const { parse, join } = require('node:path')
|
|
3
|
+
const { promises: fsp } = require('node:fs')
|
|
3
4
|
const YAML = require('yaml')
|
|
4
5
|
const { read, write } = require('../cds').utils
|
|
5
6
|
const JSONC = require('./jsonc')
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
module.exports = new class FsUtil {
|
|
8
|
+
/* YAML */
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
async function readYAML(src, project) {
|
|
11
|
+
let file
|
|
12
|
+
try {
|
|
13
|
+
file = await read(src, 'utf8')
|
|
14
|
+
} catch (e) {
|
|
15
|
+
if (e.code === 'ENOENT') return undefined
|
|
16
|
+
throw e
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
file = await read(src, 'utf8')
|
|
20
|
-
} catch (e) {
|
|
21
|
-
if (e.code === 'ENOENT') return undefined
|
|
22
|
-
throw e
|
|
23
|
-
}
|
|
24
|
-
if (project) {
|
|
25
|
-
const Mustache = require('mustache')
|
|
26
|
-
const renderedFile = Mustache.render(file, project)
|
|
27
|
-
return YAML.parseDocument(renderedFile)
|
|
28
|
-
}
|
|
29
|
-
return YAML.parseDocument(file)
|
|
18
|
+
if (project) {
|
|
19
|
+
const Mustache = require('mustache')
|
|
20
|
+
const renderedFile = Mustache.render(file, project)
|
|
21
|
+
return YAML.parseDocument(renderedFile)
|
|
30
22
|
}
|
|
23
|
+
return YAML.parseDocument(file)
|
|
24
|
+
}
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
async function writeYAML(dst, yaml) {
|
|
27
|
+
const content = YAML.stringify(yaml, { collectionStyle: 'block', lineWidth: 150, directives: false })
|
|
28
|
+
await write(content).to(dst)
|
|
29
|
+
}
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
async function copyRenderedYAML(src, dst, project) {
|
|
32
|
+
const yaml = await readYAML(src, project)
|
|
33
|
+
await write(yaml.toString()).to(dst)
|
|
34
|
+
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
const file = await read(src, 'utf8')
|
|
44
|
-
if (project) {
|
|
45
|
-
const Mustache = require('mustache')
|
|
46
|
-
const renderedFile = Mustache.render(file, project)
|
|
47
|
-
return JSON.parse(renderedFile)
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(file)
|
|
51
|
-
} catch (error) {
|
|
52
|
-
throw new Error(error.message + '\n' + file)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
36
|
+
/* JSON and JSONC */
|
|
55
37
|
|
|
56
|
-
|
|
57
|
-
|
|
38
|
+
async function readJSON(src, project) {
|
|
39
|
+
const file = await read(src, 'utf8')
|
|
40
|
+
if (project) {
|
|
41
|
+
const Mustache = require('mustache')
|
|
42
|
+
const renderedFile = Mustache.render(file, project)
|
|
43
|
+
return JSON.parse(renderedFile)
|
|
58
44
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(file)
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(error.message + '\n' + file)
|
|
63
49
|
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readJSONC(src) {
|
|
53
|
+
return JSONC.parse(await read(src, 'utf8'))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function copyRenderedJSON(src, dst, project) {
|
|
57
|
+
const json = await readJSON(src, project)
|
|
58
|
+
await write(dst, json, { spaces: 2 })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* General */
|
|
62
|
+
|
|
63
|
+
function isRoot(folder) {
|
|
64
|
+
const { root, dir } = parse(folder)
|
|
65
|
+
return root === dir
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const DEFAULT_CONCURRENCY = (() => {
|
|
69
|
+
const cpu = os.availableParallelism()
|
|
70
|
+
const uv = Number(process.env.UV_THREADPOOL_SIZE) || 4
|
|
71
|
+
// Keep libuv busy without building huge backlogs. Clamp to [8,64].
|
|
72
|
+
return Math.min(64, Math.max(8, Math.min(cpu * 4, uv * 2)))
|
|
73
|
+
})()
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Recursively finds files in a directory tree with parallel traversal.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} dir - Root directory to search
|
|
79
|
+
* @param {Object} [options] - Search options
|
|
80
|
+
* @param {(file: string) => boolean} [options.filter] - Filter predicate for paths
|
|
81
|
+
* @param {boolean} [options.ignoreSymlinks=false] - Skip symbolic links
|
|
82
|
+
* @returns {Promise<string[]>} Array of matching file paths
|
|
83
|
+
*/
|
|
84
|
+
async function find(dir, { filter, ignoreSymlinks = false } = {}) {
|
|
85
|
+
const files = /** @type {string[]} */([])
|
|
86
|
+
const queue = /** @type {string[]} */([dir])
|
|
87
|
+
const active = new Set()
|
|
88
|
+
const visit = async (/** @type {string} */ d) => {
|
|
89
|
+
let entries = /** @type {import('fs').Dirent[]} */([])
|
|
90
|
+
try { entries = await fsp.readdir(d, { withFileTypes: true }) }
|
|
91
|
+
catch { return }
|
|
92
|
+
|
|
93
|
+
await Promise.all(entries.map(async dirent => {
|
|
94
|
+
const p = join(d, dirent.name)
|
|
95
|
+
if (filter && !filter(p)) return
|
|
96
|
+
if (dirent.isDirectory()) { queue.push(p); return }
|
|
97
|
+
if (dirent.isFile()) { files.push(p); return }
|
|
98
|
+
if (!ignoreSymlinks && dirent.isSymbolicLink()) {
|
|
99
|
+
try {
|
|
100
|
+
const stat = await fsp.stat(p)
|
|
101
|
+
stat.isDirectory() ? queue.push(p) : files.push(p)
|
|
102
|
+
} catch { /* ignore */ }
|
|
103
|
+
}
|
|
104
|
+
}))
|
|
68
105
|
}
|
|
106
|
+
while (queue.length || active.size) {
|
|
107
|
+
while (queue.length && active.size < DEFAULT_CONCURRENCY) {
|
|
108
|
+
const p = visit(queue.shift()).finally(() => active.delete(p))
|
|
109
|
+
active.add(p)
|
|
110
|
+
}
|
|
111
|
+
if (active.size) await Promise.race(active)
|
|
112
|
+
}
|
|
113
|
+
return files
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
readYAML,
|
|
118
|
+
writeYAML,
|
|
119
|
+
copyRenderedYAML,
|
|
120
|
+
readJSON,
|
|
121
|
+
readJSONC,
|
|
122
|
+
copyRenderedJSON,
|
|
123
|
+
find,
|
|
124
|
+
isRoot
|
|
69
125
|
}
|
package/lib/util/glob.js
CHANGED
|
@@ -9,7 +9,8 @@ const IGNORE_DIRS = new Set(['node_modules', '.git', '.hg', '.svn', 'dist', 'tar
|
|
|
9
9
|
* Returns an AsyncIterator of those (recursive) subdirectories that match the glob pattern.
|
|
10
10
|
* Skips common large directories (node_modules, .git, .hg, .svn, dist, target, build, out).
|
|
11
11
|
* @param {string} pattern - The glob pattern to match against.
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {object} options
|
|
13
|
+
* @param {string} options.cwd - The current working directory to start the search from.
|
|
13
14
|
* @returns An array of relative paths that match the glob pattern.
|
|
14
15
|
*/
|
|
15
16
|
async function* glob(pattern, { cwd } = { cwd: process.cwd() }) {
|
package/lib/util/term.js
CHANGED
|
@@ -34,7 +34,9 @@ const format = module.exports.format = (o, severity='Error', asInternalError=fal
|
|
|
34
34
|
default : return format.info (o)
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
const forceColor = process.env.FORCE_COLOR && process.env.FORCE_COLOR !== 'false'
|
|
38
|
+
const rendersColors = process.stdout.isTTY && process.stderr.isTTY || process.env.GITHUB_ACTIONS
|
|
39
|
+
format.colors = rendersColors && process.env.NO_COLOR !== 'true' || forceColor
|
|
38
40
|
|
|
39
41
|
// decorate.error, .warning, .info
|
|
40
42
|
// 'Error: foo' -> '[ERROR] foo' (Maven-like, allows for better grepping in logs)
|
|
@@ -47,13 +49,7 @@ Object.assign (format, {
|
|
|
47
49
|
return `[${asErr('ERROR')}] ${toString(o, 'Error', withStack)}`
|
|
48
50
|
},
|
|
49
51
|
warn: o => DEBUG ? o : `[${asWarn('WARNING')}] ${toString(o, 'Warning')}`,
|
|
50
|
-
info: o => DEBUG ? o : `[${asInfo('INFO')}] ${toString(o, 'Info')}
|
|
51
|
-
|
|
52
|
-
poorMarkdown: (md) => { return md
|
|
53
|
-
.replace(/\n# ([^\n]*)\n/g, `\n${as(t.bold, '$1')}\n`)
|
|
54
|
-
.replace(/ \*([^*]+)\*/g, ` ${as(t.bold, '$1')}`)
|
|
55
|
-
.replace(/ _([^_]+)_/g, ` ${as(t.link, '$1')}`)
|
|
56
|
-
}
|
|
52
|
+
info: o => DEBUG ? o : `[${asInfo('INFO')}] ${toString(o, 'Info')}`
|
|
57
53
|
})
|
|
58
54
|
|
|
59
55
|
function toString(o, severity, withStack) {
|
package/lib/util/write.js
CHANGED
|
@@ -30,7 +30,7 @@ const write = module.exports = Object.assign (
|
|
|
30
30
|
* Fluent API to write (output) .to (dest...)
|
|
31
31
|
* @param content - the content to be written; will be JSON.stringified if it's not a string
|
|
32
32
|
*/
|
|
33
|
-
(content) => ({
|
|
33
|
+
(content) => ({
|
|
34
34
|
/**
|
|
35
35
|
* @param {string} [folder] - optional separately specified destination folder
|
|
36
36
|
* @param {string} file - the filename to write to
|
|
@@ -56,7 +56,7 @@ const write = module.exports = Object.assign (
|
|
|
56
56
|
/**
|
|
57
57
|
* @returns {Promise<[string]>} a Promise resolving to an array of all written filenames
|
|
58
58
|
*/
|
|
59
|
-
function _write (content, /*to:*/ dest, _many) {
|
|
59
|
+
function _write (content, /*to:*/ dest, _many) {
|
|
60
60
|
|
|
61
61
|
if (content instanceof Promise) return content.then (c => _write(c, dest, _many))
|
|
62
62
|
|
|
@@ -95,5 +95,3 @@ function _write (content, /*to:*/ dest, _many) { // NOSONAR
|
|
|
95
95
|
.then (()=> file)
|
|
96
96
|
|
|
97
97
|
}
|
|
98
|
-
|
|
99
|
-
/* eslint no-console:off */
|
|
@@ -5,10 +5,11 @@ const ws = require('ws')
|
|
|
5
5
|
const express = require('express')
|
|
6
6
|
const { URL } = require('url')
|
|
7
7
|
const { relative, sep } = require ('path')
|
|
8
|
+
const escapedSep = sep === '\\' ? '\\\\' : sep
|
|
8
9
|
|
|
9
10
|
const ext = 'css,gif,html,jpg,png,svg,ts,mjs,cjs,js,json,properties,change,variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change'
|
|
10
11
|
const watchIncludes = RegExp(`\\.(${ext.replace(/,/g,'|')})$|\\${sep}(webapp|appconfig)`)
|
|
11
|
-
const watchExcludes = RegExp(
|
|
12
|
+
const watchExcludes = new RegExp(`${escapedSep}(node_modules|\\.git|dist)(${escapedSep}|$)|@cds-models${escapedSep}|@types${escapedSep}`)
|
|
12
13
|
|
|
13
14
|
module.exports = class LiveReload {
|
|
14
15
|
async start(port, cwd, delay=0) {
|
|
@@ -83,14 +84,14 @@ module.exports = class LiveReload {
|
|
|
83
84
|
})
|
|
84
85
|
|
|
85
86
|
// Watching for touched files...
|
|
86
|
-
const watch = require('
|
|
87
|
+
const watch = require('./watcher')
|
|
87
88
|
const filter = f => {
|
|
88
89
|
const isIn = !watchExcludes.test(f) && watchIncludes.test(f)
|
|
89
90
|
if (!isIn) DEBUG?. (`livereload ignored: ${relative(cwd||process.cwd(), f)}`)
|
|
90
91
|
// else DEBUG?. (`livereload NOT ignored: ${relative(cwd||process.cwd(), f)}`, watchIncludes)
|
|
91
92
|
return isIn
|
|
92
93
|
}
|
|
93
|
-
const watcher = watch (cwd||process.cwd(),{
|
|
94
|
+
const watcher = watch (cwd||process.cwd(),{ filter, ignore: watchExcludes })
|
|
94
95
|
if (DEBUG) watcher.on('close', ()=> DEBUG('⚡️', 'cds watch - livereload file watcher closed'))
|
|
95
96
|
watcher.on('change', _coalesceEvents(delay, (files) => {
|
|
96
97
|
DEBUG?. (`livereload for: ${files.map(f => f.name)}`)
|
package/lib/watch/node-watch.js
CHANGED
|
@@ -49,7 +49,7 @@ module.exports = ({ cwd = process.cwd(), script, ext, ignore=RegExp(), watched,
|
|
|
49
49
|
process.on('shutdown', ()=> _kill(child))
|
|
50
50
|
|
|
51
51
|
// Watching for touched files...
|
|
52
|
-
const watch = require('
|
|
52
|
+
const watch = require('./watcher')
|
|
53
53
|
const include = RegExp(`\\.(${ext.replace(/,/g,'|')})$`)
|
|
54
54
|
const filter = f => {
|
|
55
55
|
const isIn = !ignore.test(f) && include.test(f)
|
|
@@ -57,7 +57,7 @@ module.exports = ({ cwd = process.cwd(), script, ext, ignore=RegExp(), watched,
|
|
|
57
57
|
// else DEBUG?. (`NOT ignored: ${relative(cwd||process.cwd(), f)}`, include.test(f), include)
|
|
58
58
|
return isIn
|
|
59
59
|
}
|
|
60
|
-
const watcher = watch (watched,{
|
|
60
|
+
const watcher = watch (watched,{ filter, ignore })
|
|
61
61
|
if (DEBUG) watcher.on('close', ()=> DEBUG('⚡️', 'cds watch - file watcher closed'))
|
|
62
62
|
watcher.on('change', restart)
|
|
63
63
|
process.on('shutdown', ()=> watcher.close())
|
|
@@ -78,12 +78,9 @@ module.exports = ({ cwd = process.cwd(), script, ext, ignore=RegExp(), watched,
|
|
|
78
78
|
|
|
79
79
|
emitter.restart = restart
|
|
80
80
|
emitter.stop = () => _kill(child)
|
|
81
|
-
emitter.setEnv
|
|
82
|
-
if (typeof value ===
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
env[name] // REVISIT: no value assigned ???
|
|
86
|
-
}
|
|
81
|
+
emitter.setEnv = (name, value) => {
|
|
82
|
+
if (typeof value === 'undefined') delete env[name]
|
|
83
|
+
else env[name] = value
|
|
87
84
|
}
|
|
88
85
|
return emitter
|
|
89
86
|
}
|
package/lib/watch/watched.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
}}
|
|
27
27
|
|
|
28
28
|
function _no_models_found(e) { console.log (`
|
|
29
|
-
No models found in ${e.
|
|
29
|
+
No models found in ${(e.files||cds.resolve(e.model||'*',false)).filter(f => !(f in {schema:1,services:2}))}.
|
|
30
30
|
Waiting for some to arrive...
|
|
31
31
|
`)}
|
|
32
32
|
|