@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/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
+ }