@sap/cds-dk 9.5.0 → 9.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/CHANGELOG.md +52 -0
- package/bin/cds.js +1 -1
- package/bin/compile/index.js +3 -2
- package/bin/env.js +3 -3
- package/bin/up.js +22 -16
- package/bin/util/containerize.js +27 -14
- package/lib/build/provider/internalBuildPlugin.js +23 -0
- package/lib/build/util.js +33 -16
- package/lib/import/common.js +7 -1
- package/lib/import/odata/v4parser.js +25 -38
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +7 -1
- package/lib/init/projectReader.js +21 -10
- package/lib/init/template/ams/index.js +2 -2
- package/lib/init/template/app-frontend/files/ui5.yaml.hbs +1 -0
- package/lib/init/template/app-frontend/index.js +2 -2
- package/lib/init/template/application-logging/index.js +2 -2
- package/lib/init/template/approuter/index.js +9 -5
- package/lib/init/template/attachments/index.js +4 -2
- package/lib/init/template/audit-logging/index.js +4 -2
- package/lib/init/template/cloud-logging/index.js +4 -2
- package/lib/init/template/connectivity/index.js +2 -2
- package/lib/init/template/console/files/package.json +5 -0
- package/lib/init/template/console/index.js +25 -0
- package/lib/init/template/containerize/index.js +4 -2
- package/lib/init/template/destination/index.js +4 -2
- package/lib/init/template/dynatrace/files/package.json.hbs +2 -2
- package/lib/init/template/dynatrace/index.js +4 -2
- package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +3 -1
- package/lib/init/template/enterprise-messaging/index.js +4 -2
- package/lib/init/template/enterprise-messaging-shared/index.js +4 -2
- package/lib/init/template/github-actions/files/workflows/release.yaml.hbs +2 -2
- package/lib/init/template/github-actions/index.js +4 -4
- package/lib/init/template/hana/index.js +4 -4
- package/lib/init/template/helm/index.js +6 -3
- package/lib/init/template/helm-unified-runtime/index.js +1 -0
- package/lib/init/template/html5-repo/files/ui5.yaml.hbs +1 -0
- package/lib/init/template/html5-repo/index.js +9 -7
- package/lib/init/template/ias/index.js +2 -2
- package/lib/init/template/java/index.js +14 -0
- package/lib/init/template/kafka/index.js +4 -2
- package/lib/init/template/kyma/index.js +124 -13
- package/lib/init/template/malware-scanner/index.js +4 -2
- package/lib/init/template/mta/index.js +1 -1
- package/lib/init/template/multitenancy/index.js +7 -4
- package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -3
- package/lib/init/template/pipeline/files/ui5.yaml.hbs +1 -0
- package/lib/init/template/portal/index.js +6 -3
- package/lib/init/template/postgres/index.js +2 -2
- package/lib/init/template/redis/index.js +2 -3
- package/lib/init/template/redis-messaging/index.js +2 -3
- package/lib/init/template/sample/files/_i18n/i18n_de.properties +9 -0
- package/lib/init/template/sample/files/_i18n/i18n_fr.properties +1 -0
- package/lib/init/template/sample/files/app/_i18n/i18n.properties +17 -0
- package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +16 -2
- package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +3 -11
- package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_de.properties +3 -0
- package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_en.properties +3 -0
- package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +1 -1
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +3 -11
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +3 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +3 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +1 -1
- package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +6 -6
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +3 -11
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +3 -0
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +3 -0
- package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
- package/lib/init/template/sample/files/app/common.cds +2 -1
- package/lib/init/template/sample/files/app/fiori-apps.html +32 -12
- package/lib/init/template/sample/files/app/genres/fiori-service.cds +1 -1
- package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +3 -4
- package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +3 -2
- package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_en.properties +3 -0
- package/lib/init/template/sample/files/app/genres/webapp/manifest.json +2 -2
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.texts.csv +43 -0
- package/lib/init/template/sample/index.js +14 -1
- package/lib/init/template/sqlite/files/package.json.hbs +13 -0
- package/lib/init/template/sqlite/index.js +1 -1
- package/lib/init/template/xsuaa/index.js +5 -2
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/util/checksum.js +19 -10
- package/lib/util/fs.js +8 -1
- package/lib/util/glob.js +26 -13
- package/lib/util/term.js +3 -1
- package/lib/watch/livereload-server.js +4 -3
- package/lib/watch/node-watch.js +5 -8
- package/lib/watch/watcher.js +136 -0
- package/npm-shrinkwrap.json +110 -117
- package/package.json +2 -3
- package/lib/init/template/sample/files/app/_i18n/i18n_en.properties +0 -8
- package/lib/init/template/sqlite/files/package.json +0 -5
- /package/lib/init/template/{helm → kyma}/files/Chart.yaml.hbs +0 -0
- /package/lib/init/template/{containerize → kyma}/files/containerize.yaml.hbs +0 -0
- /package/lib/init/template/{helm → kyma}/files/values.schema.json +0 -0
- /package/lib/init/template/{helm → kyma}/files/values.yaml.hbs +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
using { sap.capire.bookshop as my } from '../db/schema';
|
|
6
6
|
using { sap.common } from '@sap/cds/common';
|
|
7
|
+
using { sap.common.Currencies } from '../db/currencies';
|
|
7
8
|
|
|
8
9
|
////////////////////////////////////////////////////////////////////////////
|
|
9
10
|
//
|
|
@@ -225,7 +226,7 @@ annotate common.Currencies with @(UI : {
|
|
|
225
226
|
//
|
|
226
227
|
// Currencies Elements
|
|
227
228
|
//
|
|
228
|
-
annotate common.Currencies with {
|
|
229
|
+
annotate sap.common.Currencies with {
|
|
229
230
|
numcode @title: '{i18n>NumCode}';
|
|
230
231
|
minor @title: '{i18n>MinorUnit}';
|
|
231
232
|
exponent @title: '{i18n>Exponent}';
|
|
@@ -8,23 +8,43 @@
|
|
|
8
8
|
<title>Bookshop</title>
|
|
9
9
|
|
|
10
10
|
<script>
|
|
11
|
-
window[
|
|
12
|
-
defaultRenderer:
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
window['sap-ushell-config'] = {
|
|
12
|
+
defaultRenderer: 'fiori2',
|
|
13
|
+
renderers: {
|
|
14
|
+
fiori2: {
|
|
15
|
+
componentData: {
|
|
16
|
+
config: {
|
|
17
|
+
enableSearch: false // suppress browser errors
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
applications: {}
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script
|
|
27
|
+
id="sap-ushell-bootstrap"
|
|
28
|
+
src="https://ui5.sap.com/test-resources/sap/ushell/bootstrap/sandbox.js">
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<script
|
|
32
|
+
id="sap-ui-bootstrap"
|
|
33
|
+
src="https://ui5.sap.com/resources/sap-ui-core.js"
|
|
34
|
+
data-sap-ui-libs="sap.m,sap.ushell,sap.collaboration,sap.ui.layout"
|
|
35
|
+
data-sap-ui-compatVersion="edge"
|
|
36
|
+
data-sap-ui-theme="sap_horizon">
|
|
15
37
|
</script>
|
|
16
38
|
|
|
17
|
-
<script id="sap-ushell-bootstrap" src="https://ui5.sap.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
|
18
|
-
<script id="sap-ui-bootstrap" src="https://ui5.sap.com/resources/sap-ui-core.js"
|
|
19
|
-
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout" data-sap-ui-compatVersion="edge"
|
|
20
|
-
data-sap-ui-theme="sap_horizon"></script>
|
|
21
39
|
<script>
|
|
22
|
-
sap.ui.getCore().attachInit(() =>
|
|
23
|
-
sap.ushell.Container
|
|
24
|
-
|
|
40
|
+
sap.ui.getCore().attachInit(() => {
|
|
41
|
+
sap.ushell.Container
|
|
42
|
+
.createRenderer('fiori2', true)
|
|
43
|
+
.then(renderer => renderer.placeAt('content'))
|
|
44
|
+
})
|
|
25
45
|
</script>
|
|
26
46
|
</head>
|
|
27
47
|
|
|
28
48
|
<body class="sapUiBody" id="content"></body>
|
|
29
49
|
|
|
30
|
-
</html>
|
|
50
|
+
</html>
|
|
@@ -28,6 +28,6 @@ annotate Genres with @(UI : {
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
// Tree Views
|
|
31
|
-
// annotate AdminService.Genres with @hierarchy; //
|
|
31
|
+
// annotate AdminService.Genres with @hierarchy; // upcoming simplification
|
|
32
32
|
using from './tree-view';
|
|
33
33
|
using from './value-help';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
appDescription=Genres as Tree View
|
|
1
|
+
appTitle = Browse Genres
|
|
2
|
+
appSubTitle = CAP Sample Application
|
|
3
|
+
appDescription = Genres as Tree View
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
appTitle=Zeige Genres
|
|
2
|
-
|
|
1
|
+
appTitle = Zeige Genres
|
|
2
|
+
appSubTitle = CAP Beispielanwendung
|
|
3
|
+
appDescription = Genres als Baumansicht
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"additionalParameters": "allowed"
|
|
26
26
|
},
|
|
27
27
|
"title": "{{appTitle}}",
|
|
28
|
-
"subTitle": "{{
|
|
28
|
+
"subTitle": "{{appSubTitle}}",
|
|
29
29
|
"semanticObject": "Genres",
|
|
30
30
|
"action": "display"
|
|
31
31
|
}
|
|
@@ -123,4 +123,4 @@
|
|
|
123
123
|
"registrationIds": [],
|
|
124
124
|
"archeType": "transactional"
|
|
125
125
|
}
|
|
126
|
-
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ID,locale,name
|
|
2
|
+
10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Belletristik
|
|
3
|
+
11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Drama
|
|
4
|
+
12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Lyrik
|
|
5
|
+
13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Fantasy
|
|
6
|
+
131aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Märchen
|
|
7
|
+
132aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Epische Fantasy
|
|
8
|
+
133aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,High Fantasy
|
|
9
|
+
134aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Schauerroman
|
|
10
|
+
14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Science-Fiction
|
|
11
|
+
141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Utopien und Dystopien
|
|
12
|
+
1411aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Utopie
|
|
13
|
+
1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Dystopie
|
|
14
|
+
14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Cyberpunk
|
|
15
|
+
141211aa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Steampunk
|
|
16
|
+
142aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Space Opera
|
|
17
|
+
143aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Zeitreise
|
|
18
|
+
144aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Tech-Noir
|
|
19
|
+
15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Liebesroman
|
|
20
|
+
151aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Zeitgenössischer Liebesroman
|
|
21
|
+
152aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Historischer Liebesroman
|
|
22
|
+
153aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Romantischer Thriller
|
|
23
|
+
16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Krimi
|
|
24
|
+
161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Kriminalroman
|
|
25
|
+
1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Thriller
|
|
26
|
+
16111aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Polizeikrimi
|
|
27
|
+
16112aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Justizthriller
|
|
28
|
+
16113aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Medizinthriller
|
|
29
|
+
16114aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Spionagethriller
|
|
30
|
+
1612aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Detektivroman
|
|
31
|
+
1613aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Spannung
|
|
32
|
+
162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Noir
|
|
33
|
+
1621aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Nordic Noir
|
|
34
|
+
1622aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Tart Noir
|
|
35
|
+
163aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Cosy Crime
|
|
36
|
+
17aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Abenteuer
|
|
37
|
+
18aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Kurzgeschichte
|
|
38
|
+
19aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Graphic Novel
|
|
39
|
+
20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Sachbuch
|
|
40
|
+
21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Biografie
|
|
41
|
+
22aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Autobiografie
|
|
42
|
+
23aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Essay
|
|
43
|
+
24aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,de,Rede
|
|
@@ -49,11 +49,24 @@ module.exports = class SampleTemplate extends require('../../plugin') {
|
|
|
49
49
|
|
|
50
50
|
await merge(__dirname, 'files/package.json.hbs').into('package.json', { with: { appPath }})
|
|
51
51
|
|
|
52
|
-
// manifest.json must be unique
|
|
52
|
+
// manifest.json IDs must be unique -> otherwise deployment failures on conflicts
|
|
53
53
|
await Promise.all(project.appUIPaths.map(async p => {
|
|
54
54
|
const manifest = await read(join(appPath, p, 'webapp/manifest.json'))
|
|
55
55
|
manifest['sap.app'].id = appName + '.' + p
|
|
56
56
|
await write(join(appPath, p, 'webapp/manifest.json'), manifest, { spaces: 2 })
|
|
57
57
|
}))
|
|
58
|
+
|
|
59
|
+
if (project.isNodejs) { // REVISIT: align with Java samples
|
|
60
|
+
const fioriSandboxConfigPath = join(appPath, 'appconfig/fioriSandboxConfig.json')
|
|
61
|
+
const fioriSandboxConfig = await read(fioriSandboxConfigPath)
|
|
62
|
+
const sampleInbounds = { BrowseBooks: 'browse', ManageAuthors: 'admin-authors', BrowseGenres: 'genres', ManageBooks: 'admin-books' }
|
|
63
|
+
for (const [name, id] of Object.entries(sampleInbounds)) {
|
|
64
|
+
const service = fioriSandboxConfig.services?.ClientSideTargetResolution?.adapter?.config?.inbounds?.[name]
|
|
65
|
+
if (!service) continue
|
|
66
|
+
if (!service.resolutionResult) service.resolutionResult = {}
|
|
67
|
+
service.resolutionResult.additionalInformation = `SAPUI5.Component=${appName}.${id}`
|
|
68
|
+
await write(fioriSandboxConfigPath, fioriSandboxConfig, { spaces: 2 })
|
|
69
|
+
}
|
|
70
|
+
}
|
|
58
71
|
}
|
|
59
72
|
}
|
|
@@ -12,7 +12,7 @@ module.exports = class SQLiteTemplate extends require('../../plugin') {
|
|
|
12
12
|
const project = readProject()
|
|
13
13
|
const { isNodejs, isJava, configFile } = project
|
|
14
14
|
if (isNodejs) {
|
|
15
|
-
await merge(__dirname, 'files/package.json').into(configFile)
|
|
15
|
+
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
16
16
|
} else if (isJava) {
|
|
17
17
|
await mvn.add('sqlite')
|
|
18
18
|
}
|
|
@@ -3,6 +3,7 @@ const { exists, read } = cds.utils
|
|
|
3
3
|
const { readProject } = require('../../projectReader')
|
|
4
4
|
const { merge } = require('../../merge')
|
|
5
5
|
const { srv4, xsuaa: xsuaaDefault, xsuaa4, mtxSidecar4 } = require('../../registries/mta')
|
|
6
|
+
const KymaTemplate = require('../kyma')
|
|
6
7
|
|
|
7
8
|
module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
8
9
|
|
|
@@ -33,7 +34,7 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
|
33
34
|
|
|
34
35
|
async combine() {
|
|
35
36
|
const project = readProject()
|
|
36
|
-
const { addMultitenancy, hasMultitenancy, addMta, hasMta,
|
|
37
|
+
const { addMultitenancy, hasMultitenancy, addMta, hasMta, addKyma, isJava, srvPath, hasKyma } = project
|
|
37
38
|
|
|
38
39
|
const cds = require('../../../../lib')
|
|
39
40
|
const xsSecurity = await (async () => {
|
|
@@ -82,7 +83,7 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
|
82
83
|
await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { project, additions, overwrites, relationships })
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
if (
|
|
86
|
+
if (addKyma || (addMultitenancy && hasKyma)) {
|
|
86
87
|
const overwrites = addMultitenancy ? [{ item: 'xsuaa.parameters.tenant-mode', withValue: 'shared' }] : []
|
|
87
88
|
await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project, overwrites })
|
|
88
89
|
await merge({
|
|
@@ -92,6 +93,8 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
}).into('chart/values.yaml')
|
|
96
|
+
await KymaTemplate.mergeDependency('service-instance', 'xsuaa')
|
|
97
|
+
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
}
|
package/lib/lint/eslintHelp.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "9.39.
|
|
2
|
+
"version": "9.39.2",
|
|
3
3
|
"help": "\n# SYNOPSIS\n\n *cds lint* [options] file.js [file.js] [dir]\n\n Runs environment checks and/or checks the specified models\n based on the ESLint framework\n\n# OPTIONS\n \n Basic configuration:\n --no-config-lookup Disable look up for eslint.config.js\n -c, --config path::String Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs\n --inspect-config Open the config inspector with the current configuration\n --ext [String] Specify additional file extensions to lint\n --global [String] Define global variables\n --parser String Specify the parser to be used\n --parser-options Object Specify parser options\n \n Specify Rules and Plugins:\n --plugin [String] Specify plugins\n --rule Object Specify rules\n \n Fix Problems:\n --fix Automatically fix problems\n --fix-dry-run Automatically fix problems without saving the changes to the file system\n --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout)\n \n Ignore Files:\n --no-ignore Disable use of ignore files and patterns\n --ignore-pattern [String] Patterns of files to ignore\n \n Use stdin:\n --stdin Lint code provided on <STDIN> - default: false\n --stdin-filename String Specify filename to process STDIN as\n \n Handle Warnings:\n --quiet Report errors only - default: false\n --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1\n \n Output:\n -o, --output-file path::String Specify file to write report to\n -f, --format String Use a specific output format - default: stylish\n --color, --no-color Force enabling/disabling of color\n \n Inline configuration comments:\n --no-inline-config Prevent comments from changing config or rules\n --report-unused-disable-directives Adds reported errors for unused eslint-disable and eslint-enable directives\n --report-unused-disable-directives-severity String Chooses severity level for reporting unused eslint-disable and eslint-enable directives - either: off, warn, error, 0, 1, or 2\n --report-unused-inline-configs String Adds reported errors for unused eslint inline config comments - either: off, warn, error, 0, 1, or 2\n \n Caching:\n --cache Only check changed files - default: false\n --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache\n --cache-location path::String Path to the cache file or directory\n --cache-strategy String Strategy to use for detecting changed files in the cache - either: metadata or content - default: metadata\n \n Suppressing Violations:\n --suppress-all Suppress all violations - default: false\n --suppress-rule [String] Suppress specific rules\n --suppressions-location path::String Specify the location of the suppressions file\n --prune-suppressions Prune unused suppressions - default: false\n --pass-on-unpruned-suppressions Ignore unused suppressions - default: false\n \n Miscellaneous:\n --init Run config initialization wizard - default: false\n --env-info Output execution environment information - default: false\n --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched\n --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false\n --no-warn-ignored Suppress warnings when the file list includes ignored files\n --pass-on-no-patterns Exit with exit code 0 in case no file patterns are passed\n --debug Output debugging information\n -h, --help Show help\n -v, --version Output the version number\n --print-config path::String Print the configuration for the given file\n --stats Add statistics to the lint report - default: false\n --flag [String] Enable a feature flag\n --mcp Start the ESLint MCP server\n --concurrency Int|String Number of linting threads, auto to choose automatically, off for no multithreading - default: off\n \n",
|
|
4
4
|
"options": [
|
|
5
5
|
"--config",
|
package/lib/util/checksum.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../../lib/cds')
|
|
2
|
-
const { read } = cds.utils
|
|
2
|
+
const { read, stat } = cds.utils
|
|
3
3
|
const crypto = require('crypto')
|
|
4
4
|
const { getFiles } = require('../build/util')
|
|
5
5
|
|
|
@@ -12,18 +12,27 @@ const { getFiles } = require('../build/util')
|
|
|
12
12
|
*/
|
|
13
13
|
async function calcDirChecksum(dirPath, filter = [], hashFunction = 'sha256', optionalFiles = []) {
|
|
14
14
|
const hash = crypto.createHash(hashFunction)
|
|
15
|
-
const files = await getFiles(dirPath, filter)
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
let stats
|
|
17
|
+
try {
|
|
18
|
+
stats = await stat(dirPath)
|
|
19
|
+
} catch {
|
|
20
|
+
return hash.digest('hex')
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
hash.
|
|
26
|
-
|
|
23
|
+
if (stats.isFile()) {
|
|
24
|
+
hash.update(await read(dirPath, "binary"))
|
|
25
|
+
return hash.digest('hex')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const files = [...(await getFiles(dirPath, filter, [], true)), ...optionalFiles].sort()
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
try {
|
|
31
|
+
hash.update(file)
|
|
32
|
+
hash.update(await read(file, "binary"))
|
|
33
|
+
} catch {
|
|
34
|
+
// ignore
|
|
35
|
+
}
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
return hash.digest('hex')
|
package/lib/util/fs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
const { parse } = require('node:path')
|
|
2
|
+
|
|
1
3
|
const YAML = require('yaml')
|
|
2
4
|
const { read, write } = require('../cds').utils
|
|
3
|
-
const JSONC = require('./jsonc')
|
|
5
|
+
const JSONC = require('./jsonc')
|
|
4
6
|
|
|
5
7
|
// REVISIT: Consider merging with template util
|
|
6
8
|
|
|
@@ -59,4 +61,9 @@ module.exports = new class FsUtil {
|
|
|
59
61
|
const json = await this.readJSON(src, project)
|
|
60
62
|
await write(dst, json, { spaces: 2 })
|
|
61
63
|
}
|
|
64
|
+
|
|
65
|
+
isRoot(folder) {
|
|
66
|
+
const { root, dir } = parse(folder)
|
|
67
|
+
return root === dir
|
|
68
|
+
}
|
|
62
69
|
}
|
package/lib/util/glob.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
const path = require('node:path');
|
|
2
2
|
const { join } = path;
|
|
3
|
-
const { existsSync } = require('node:fs');
|
|
4
3
|
const { readdir } = require('node:fs').promises;
|
|
5
4
|
|
|
5
|
+
const IGNORE_DIRS = new Set(['node_modules', '.git', '.hg', '.svn', 'dist', 'target', 'build', 'out']);
|
|
6
|
+
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Returns an AsyncIterator of those (recursive) subdirectories that match the glob pattern.
|
|
8
|
-
*
|
|
9
|
-
* @param
|
|
10
|
-
* @
|
|
10
|
+
* Skips common large directories (node_modules, .git, .hg, .svn, dist, target, build, out).
|
|
11
|
+
* @param {string} pattern - The glob pattern to match against.
|
|
12
|
+
* @param {string} cwd - The current working directory to start the search from.
|
|
13
|
+
* @returns An array of relative paths that match the glob pattern.
|
|
11
14
|
*/
|
|
12
15
|
async function* glob(pattern, { cwd } = { cwd: process.cwd() }) {
|
|
13
16
|
if (/\{.*}/.test(pattern)) {
|
|
@@ -18,32 +21,42 @@ async function* glob(pattern, { cwd } = { cwd: process.cwd() }) {
|
|
|
18
21
|
}
|
|
19
22
|
return globNative(pattern, { cwd });
|
|
20
23
|
}
|
|
21
|
-
yield* (await globArray(pattern,
|
|
24
|
+
yield* (await globArray(pattern, cwd));
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* Returns those subdirectories, recursively, that match the glob pattern.
|
|
26
|
-
*
|
|
27
|
-
* @param
|
|
29
|
+
* Skips common large directories (node_modules, .git, .hg, .svn, dist, target, build, out).
|
|
30
|
+
* @param {string} pattern - The glob pattern to match against.
|
|
31
|
+
* @param {string} cwd - The current working directory to start the search from.
|
|
28
32
|
* @returns {Promise<string[]>} - An array of relative paths that match the glob pattern.
|
|
29
33
|
*/
|
|
30
|
-
async function globArray(pattern,
|
|
34
|
+
async function globArray(pattern, cwd) {
|
|
31
35
|
const regex = globToRegex(pattern);
|
|
32
36
|
return (await subdirs(cwd))
|
|
33
37
|
.map(fullPath => path.relative(cwd, fullPath))
|
|
34
|
-
.filter(relativePath => regex.test(relativePath));
|
|
38
|
+
.filter(relativePath => regex.test(relativePath.replace(/\\/g, '/')));
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
42
|
* Recursively returns all subdirectories of a given directory.
|
|
39
43
|
* Returns only non-hidden directories (those not starting with a dot).
|
|
40
|
-
*
|
|
44
|
+
* Skips common large directories like node_modules, .git, etc.
|
|
45
|
+
* @param {string} dir - The directory to search in.
|
|
41
46
|
* @returns {Promise<String[]>} - An array of paths to subdirectories.
|
|
42
47
|
*/
|
|
43
48
|
async function subdirs(dir) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
let entries;
|
|
50
|
+
try {
|
|
51
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const children = entries.filter(child =>
|
|
57
|
+
child.isDirectory() && !child.name.startsWith('.') && !IGNORE_DIRS.has(child.name)
|
|
58
|
+
);
|
|
59
|
+
|
|
47
60
|
return (await Promise.all(
|
|
48
61
|
children.map(async child => {
|
|
49
62
|
const childPath = join(dir, child.name);
|
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)
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const fs = require('node:fs')
|
|
2
|
+
const { resolve, sep } = require('node:path')
|
|
3
|
+
const { EventEmitter } = require('node:events')
|
|
4
|
+
|
|
5
|
+
module.exports = function watch(paths, { filter, ignore } = {}) {
|
|
6
|
+
const ee = new EventEmitter()
|
|
7
|
+
const roots = [...new Set((Array.isArray(paths) ? paths : [paths]).filter(Boolean).map(p => resolve(p)))]
|
|
8
|
+
const ac = new AbortController() // eslint-disable-line no-undef
|
|
9
|
+
let closed = false
|
|
10
|
+
|
|
11
|
+
const _allow = p => !filter || (filter instanceof RegExp ? filter.test(p) : filter(p))
|
|
12
|
+
|
|
13
|
+
const useNativeRecursive = process.platform === 'darwin' || process.platform === 'win32'
|
|
14
|
+
if (useNativeRecursive) {
|
|
15
|
+
const _watch = async root => {
|
|
16
|
+
try {
|
|
17
|
+
if (!fs.existsSync(root)) return
|
|
18
|
+
const watcher = fs.promises.watch(root, { recursive: true, signal: ac.signal })
|
|
19
|
+
for await (const event of watcher) {
|
|
20
|
+
if (closed) break
|
|
21
|
+
const name = event.filename?.toString?.() ?? event.filename ?? ''
|
|
22
|
+
const fullPath = resolve(root, name)
|
|
23
|
+
if (_allow(fullPath)) {
|
|
24
|
+
ee.emit('change', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch (e) {
|
|
28
|
+
if (e.name !== 'AbortError') ee.emit('error', e)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
roots.forEach(r => _watch(r).catch(e => ee.emit('error', e)))
|
|
33
|
+
|
|
34
|
+
ee.close = () => {
|
|
35
|
+
if (closed) return
|
|
36
|
+
closed = true
|
|
37
|
+
ac.abort()
|
|
38
|
+
ee.emit('close')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return ee
|
|
42
|
+
} else {
|
|
43
|
+
return _linuxCompat()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// On Linux, recursive watchers allocates OS watchers for the whole tree,
|
|
47
|
+
// so we avoid it to skip ignored dirs before allocating watchers.
|
|
48
|
+
function _linuxCompat() {
|
|
49
|
+
const watching = new Set()
|
|
50
|
+
const queued = new Set()
|
|
51
|
+
const queue = []
|
|
52
|
+
const active = new Set()
|
|
53
|
+
const max = 32
|
|
54
|
+
|
|
55
|
+
const _ignored = p => {
|
|
56
|
+
if (!ignore) return false
|
|
57
|
+
const test = ignore instanceof RegExp ? s => ignore.test(s) : ignore
|
|
58
|
+
return test(p) || test(p + sep) // allow trailing sep
|
|
59
|
+
}
|
|
60
|
+
const _enqueue = d => {
|
|
61
|
+
if (closed) return
|
|
62
|
+
const dir = resolve(d)
|
|
63
|
+
if (_ignored(dir)) return
|
|
64
|
+
if (watching.has(dir) || queued.has(dir)) return
|
|
65
|
+
queued.add(dir)
|
|
66
|
+
queue.push(dir)
|
|
67
|
+
_pump()
|
|
68
|
+
}
|
|
69
|
+
const _startWatch = dir => {
|
|
70
|
+
if (watching.has(dir)) return
|
|
71
|
+
watching.add(dir)
|
|
72
|
+
;(async () => {
|
|
73
|
+
try {
|
|
74
|
+
if (!fs.existsSync(dir)) return
|
|
75
|
+
const watcher = fs.promises.watch(dir, { signal: ac.signal }) // non-recursive
|
|
76
|
+
for await (const event of watcher) {
|
|
77
|
+
if (closed) break
|
|
78
|
+
const name = event.filename?.toString?.() ?? event.filename ?? ''
|
|
79
|
+
const fullPath = name ? resolve(dir, name) : dir
|
|
80
|
+
|
|
81
|
+
// if a new dir appears, start watching it (unless ignored)
|
|
82
|
+
if (name && !_ignored(fullPath) && fs.existsSync(fullPath)) {
|
|
83
|
+
try {
|
|
84
|
+
if (fs.lstatSync(fullPath).isDirectory()) _enqueue(fullPath)
|
|
85
|
+
} catch { /* ignore */ }
|
|
86
|
+
}
|
|
87
|
+
if (_allow(fullPath)) {
|
|
88
|
+
ee.emit('change', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
if (e.name !== 'AbortError' && !closed) ee.emit('error', e)
|
|
93
|
+
}
|
|
94
|
+
})().catch(e => ee.emit('error', e))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const _scan = async dir => {
|
|
98
|
+
_startWatch(dir)
|
|
99
|
+
|
|
100
|
+
let entries = []
|
|
101
|
+
try { entries = await fs.promises.readdir(dir, { withFileTypes: true }) }
|
|
102
|
+
catch { return }
|
|
103
|
+
|
|
104
|
+
for (const e of entries) {
|
|
105
|
+
if (e.isSymbolicLink?.()) continue
|
|
106
|
+
if (!e.isDirectory?.()) continue
|
|
107
|
+
const child = resolve(dir, e.name)
|
|
108
|
+
if (!_ignored(child)) _enqueue(child)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const _pump = () => {
|
|
113
|
+
while (!closed && queue.length && active.size < max) {
|
|
114
|
+
const dir = queue.shift()
|
|
115
|
+
queued.delete(dir)
|
|
116
|
+
|
|
117
|
+
const p = _scan(dir)
|
|
118
|
+
.catch(e => ee.emit('error', e))
|
|
119
|
+
.finally(() => {
|
|
120
|
+
active.delete(p)
|
|
121
|
+
_pump()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
active.add(p)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
roots.forEach(_enqueue)
|
|
128
|
+
ee.close = () => {
|
|
129
|
+
if (closed) return
|
|
130
|
+
closed = true
|
|
131
|
+
ac.abort()
|
|
132
|
+
ee.emit('close')
|
|
133
|
+
}
|
|
134
|
+
return ee
|
|
135
|
+
}
|
|
136
|
+
}
|