@cap-js/cds-typer 0.2.5-beta.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 +43 -0
- package/LICENSE +201 -0
- package/README.md +192 -0
- package/index.js +5 -0
- package/lib/cli.js +100 -0
- package/lib/compile.d.ts +273 -0
- package/lib/compile.js +926 -0
- package/lib/components/inline.js +217 -0
- package/lib/file.d.ts +208 -0
- package/lib/file.js +497 -0
- package/lib/logging.d.ts +50 -0
- package/lib/logging.js +73 -0
- package/lib/util.d.ts +87 -0
- package/lib/util.js +196 -0
- package/library/cds.hana.ts +15 -0
- package/package.json +50 -0
package/lib/util.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/* eslint-disable indent */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef { {name?: string, '@singular'?: string, '@plural'?: string,} } Annotations
|
|
5
|
+
* @typedef { {desc: string, default?: any} } CommandlineFlag
|
|
6
|
+
* @typedef { {positional: string[], named: Object<string, any>} } ParsedFlags
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// inflection functions are stolen from github/cap/dev/blob/main/etc/inflect.js
|
|
10
|
+
|
|
11
|
+
// MONKEY PATCH to support Node v14
|
|
12
|
+
// Remove at end of LTE period.
|
|
13
|
+
if (process.version.startsWith('v14')) {
|
|
14
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
15
|
+
Object.hasOwn = Object.hasOwn ?? ((obj, attr) => Boolean(obj && obj.hasOwnProperty(attr)))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const last = /\w+$/
|
|
19
|
+
|
|
20
|
+
const annotations = {
|
|
21
|
+
singular: ['@singular'],
|
|
22
|
+
plural: ['@plural'],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tries to retrieve an annotation that specifies the singular name
|
|
27
|
+
* from a CSN. Valid annotations are listed in util.annotations
|
|
28
|
+
* and their precedence is in order of definition.
|
|
29
|
+
* If no singular is specified at all, undefined is returned.
|
|
30
|
+
* @param {Object} csn the CSN of an entity to check
|
|
31
|
+
* @returns {string | undefined} the singular annotation or undefined
|
|
32
|
+
*/
|
|
33
|
+
const getSingularAnnotation = (csn) => csn[annotations.singular.find(a => Object.hasOwn(csn, a))]
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Tries to retrieve an annotation that specifies the plural name
|
|
37
|
+
* from a CSN. Valid annotations are listed in util.annotations
|
|
38
|
+
* and their precedence is in order of definition.
|
|
39
|
+
* If no plural is specified at all, undefined is returned.
|
|
40
|
+
* @param {Object} csn the CSN of an entity to check
|
|
41
|
+
* @returns {string | undefined} the plural annotation or undefined
|
|
42
|
+
*/
|
|
43
|
+
const getPluralAnnotation = (csn) => csn[annotations.plural.find(a => Object.hasOwn(csn, a))]
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Users can specify that they want to refer to localisation
|
|
47
|
+
* using the syntax {i18n>Foo}, where Foo is the name of the
|
|
48
|
+
* entity as found in the .cds file
|
|
49
|
+
* (see: https://pages.github.tools.sap/cap/docs/guides/i18n)
|
|
50
|
+
* As this throws off the naming, we remove this wrapper
|
|
51
|
+
* unlocalize("{i18n>Foo}") -> "Foo"
|
|
52
|
+
* @param {string} name the entity name (singular or plural).
|
|
53
|
+
* @returns {string} the name without localisation syntax or untouched.
|
|
54
|
+
*/
|
|
55
|
+
const unlocalize = (name) => {
|
|
56
|
+
const match = name.match(/\{i18n>(.*)\}/)
|
|
57
|
+
return match ? match[1] : name
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Attempts to derive the singular form of an English noun.
|
|
62
|
+
* If '@singular' is passed as annotation, that is preferred.
|
|
63
|
+
* @param {Annotations} dn annotations
|
|
64
|
+
* @param {boolean} stripped if true, leading namespace will be stripped
|
|
65
|
+
*/
|
|
66
|
+
const singular4 = (dn, stripped) => {
|
|
67
|
+
let n = dn.name || dn
|
|
68
|
+
if (stripped) {
|
|
69
|
+
n = n.match(last)[0]
|
|
70
|
+
}
|
|
71
|
+
return (
|
|
72
|
+
getSingularAnnotation(dn) ||
|
|
73
|
+
(/.*species|news$/i.test(n)
|
|
74
|
+
? n
|
|
75
|
+
: /.*ess$/.test(n)
|
|
76
|
+
? n // Address
|
|
77
|
+
: /.*ees$/.test(n)
|
|
78
|
+
? n.slice(0, -1) // Employees --> Employee
|
|
79
|
+
: /.*[sz]es$/.test(n)
|
|
80
|
+
? n.slice(0, -2)
|
|
81
|
+
: /.*[^aeiou]ies$/.test(n)
|
|
82
|
+
? n.slice(0, -3) + 'y' // Deliveries --> Delivery
|
|
83
|
+
: /.*s$/.test(n)
|
|
84
|
+
? n.slice(0, -1)
|
|
85
|
+
: /.*_$/.test(n) // special cdstyper case where we revert the _ suffix for when a plural can not be determined
|
|
86
|
+
? n.slice(0, -1)
|
|
87
|
+
: n
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Attempts to derive the plural form of an English noun.
|
|
94
|
+
* If '@plural' is passed as annotation, that is preferred.
|
|
95
|
+
* @param {Annotations} dn annotations
|
|
96
|
+
* @param {boolean} stripped if true, leading namespace will be stripped
|
|
97
|
+
*/
|
|
98
|
+
const plural4 = (dn, stripped) => {
|
|
99
|
+
let n = dn.name || dn
|
|
100
|
+
if (stripped) {
|
|
101
|
+
n = n.match(last)[0]
|
|
102
|
+
}
|
|
103
|
+
return (
|
|
104
|
+
getPluralAnnotation(dn) ||
|
|
105
|
+
(/.*analysis|status|species|news$/i.test(n)
|
|
106
|
+
? n
|
|
107
|
+
: /.*[^aeiou]y$/.test(n)
|
|
108
|
+
? n.slice(0, -1) + 'ies'
|
|
109
|
+
: /.*(s|x|z|ch|sh)$/.test(n)
|
|
110
|
+
? n + 'es'
|
|
111
|
+
: n + 's')
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Performs a deep merge of the passed objects into the first object.
|
|
117
|
+
* See Object.assign(target, source).
|
|
118
|
+
* @param {Object} target object to assign into.
|
|
119
|
+
* @param {Object} source object to assign from.
|
|
120
|
+
*/
|
|
121
|
+
const deepMerge = (target, source) => {
|
|
122
|
+
Object.keys(target)
|
|
123
|
+
.filter((k) => k in source)
|
|
124
|
+
.forEach((k) => deepMerge(target[k], source[k]))
|
|
125
|
+
Object.assign(target, source)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Parses command line arguments into named and positional parameters.
|
|
130
|
+
* Named parameters are expected to start with a double dash (--).
|
|
131
|
+
* If the next argument `B` after a named parameter `A` is not a named parameter itself,
|
|
132
|
+
* `B` is used as value for `A`.
|
|
133
|
+
* If `A` and `B` are both named parameters, `A` is just treated as a flag (and may receive a default value).
|
|
134
|
+
* Only named parameters that occur in validFlags are allowed. Specifying named flags that are not listed there
|
|
135
|
+
* will cause an error.
|
|
136
|
+
* Named parameters that are either not specified or do not have a value assigned to them may draw a default value
|
|
137
|
+
* from their definition in validFlags.
|
|
138
|
+
* @param {string[]} argv list of command line arguments
|
|
139
|
+
* @param {Object<string, CommandlineFlag>} validFlags allowed flags. May specify default values.
|
|
140
|
+
* @returns {ParsedFlags}
|
|
141
|
+
*/
|
|
142
|
+
const parseCommandlineArgs = (argv, validFlags) => {
|
|
143
|
+
const isFlag = (arg) => arg.startsWith('--')
|
|
144
|
+
const positional = []
|
|
145
|
+
const named = {}
|
|
146
|
+
|
|
147
|
+
let i = 0
|
|
148
|
+
while (i < argv.length) {
|
|
149
|
+
let arg = argv[i]
|
|
150
|
+
if (isFlag(arg)) {
|
|
151
|
+
arg = arg.slice(2)
|
|
152
|
+
if (!(arg in validFlags)) {
|
|
153
|
+
throw new Error(`invalid named flag '${arg}'`)
|
|
154
|
+
} else {
|
|
155
|
+
const next = argv[i + 1]
|
|
156
|
+
if (next && !isFlag(next)) {
|
|
157
|
+
named[arg] = next
|
|
158
|
+
i++
|
|
159
|
+
} else {
|
|
160
|
+
named[arg] = validFlags[arg].default
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const allowed = validFlags[arg].allowed
|
|
164
|
+
if (allowed && !allowed.includes(named[arg])) {
|
|
165
|
+
throw new Error(`invalid value '${named[arg]}' for flag ${arg}. Must be one of ${allowed.join(', ')}`)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
positional.push(arg)
|
|
170
|
+
}
|
|
171
|
+
i++
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const defaults = Object.entries(validFlags)
|
|
175
|
+
.filter((e) => !!e[1].default)
|
|
176
|
+
.reduce((dict, [k, v]) => {
|
|
177
|
+
dict[k] = v.default
|
|
178
|
+
return dict
|
|
179
|
+
}, {})
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
named: Object.assign(defaults, named),
|
|
183
|
+
positional,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
annotations,
|
|
189
|
+
getSingularAnnotation,
|
|
190
|
+
getPluralAnnotation,
|
|
191
|
+
unlocalize,
|
|
192
|
+
singular4,
|
|
193
|
+
plural4,
|
|
194
|
+
parseCommandlineArgs,
|
|
195
|
+
deepMerge,
|
|
196
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// cds-namespace: cds.hana
|
|
2
|
+
export class SMALLINT extends Number {};
|
|
3
|
+
export class TINYINT extends Number {};
|
|
4
|
+
export class SMALLDECIMAL extends Number {};
|
|
5
|
+
export class REAL extends Number {};
|
|
6
|
+
export class CHAR extends String {};
|
|
7
|
+
export class NCHAR extends String {};
|
|
8
|
+
export class VARCHAR extends String {};
|
|
9
|
+
export class CLOB extends String {};
|
|
10
|
+
export class BINARY extends String {}
|
|
11
|
+
export class ST_POINT {
|
|
12
|
+
public x: number;
|
|
13
|
+
public y: number;
|
|
14
|
+
}
|
|
15
|
+
export class ST_GEOMETRY { /* FIXME */ }
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cap-js/cds-typer",
|
|
3
|
+
"version": "0.2.5-beta.1",
|
|
4
|
+
"description": "Generates .ts files for a CDS model to receive code completion in VS Code",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"homepage": "https://cap.cloud.sap/",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"CAP",
|
|
9
|
+
"CDS",
|
|
10
|
+
"CLI"
|
|
11
|
+
],
|
|
12
|
+
"author": "SAP SE (https://www.sap.com)",
|
|
13
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test:unit": "jest --projects test/unit.jest.config.js",
|
|
16
|
+
"test:integration": "jest --projects test/int.jest.config.js",
|
|
17
|
+
"test:all": "jest",
|
|
18
|
+
"test": "npm run test:unit",
|
|
19
|
+
"lint": "eslint",
|
|
20
|
+
"cli": "node lib/cli.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"lib/",
|
|
24
|
+
"library",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"index.js",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"types": "index.d.ts",
|
|
31
|
+
"bin": {
|
|
32
|
+
"cds-typer": "./lib/cli.js"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@sap/cds": ">=6"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"eslint": "^8.15.0",
|
|
39
|
+
"eslint-config-prettier": "^8.5.0",
|
|
40
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
41
|
+
"jest": "^29",
|
|
42
|
+
"typescript": ">=4.6.4"
|
|
43
|
+
},
|
|
44
|
+
"jest": {
|
|
45
|
+
"projects": [
|
|
46
|
+
"test/unit.jest.config.js",
|
|
47
|
+
"test/int.jest.config.js"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
}
|