@cap-js/cds-typer 0.25.0 → 0.27.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/lib/config.js ADDED
@@ -0,0 +1,117 @@
1
+ const cds = require('@sap/cds')
2
+ const { camelToSnake } = require('./util')
3
+
4
+ /**
5
+ * Makes properties of an object accessible in both camelCase and snake_case.
6
+ * Snake_case gets precedence over camelCase.
7
+ * @template T
8
+ * @param {T} target - The object to proxy.
9
+ * @returns {T} - The proxied object.
10
+ */
11
+ const camelSnakeHybrid = target => {
12
+ // @ts-expect-error - expecting target to be of type {}, which is not T (same for following)
13
+ const proxy = new Proxy(target, {
14
+ get(target, prop) {
15
+ // @ts-expect-error
16
+ return target[camelToSnake(prop)] ?? target[prop]
17
+ },
18
+ set(target, p, v) {
19
+ // @ts-expect-error
20
+ target[camelToSnake(p)] = v
21
+ return true
22
+ }
23
+ })
24
+ // need to make sure all properties are initially available in snake_case
25
+ // @ts-expect-error
26
+ for (const [k,v] of Object.entries(target)) {
27
+ // @ts-expect-error
28
+ proxy[k] = v
29
+ }
30
+ // @ts-expect-error
31
+ return proxy
32
+ }
33
+ class Config {
34
+ static #defaults = {
35
+ propertiesOptional: true,
36
+ useEntitiesProxy: false,
37
+ inlineDeclarations: 'flat'
38
+ }
39
+
40
+ values = undefined
41
+ proxy = undefined
42
+
43
+ init () {
44
+ this.values = {...Config.#defaults, ...(cds.env.typer ?? {})}
45
+ this.proxy = camelSnakeHybrid(this.values)
46
+ }
47
+
48
+ constructor() {
49
+ // proxy around config still allows arbitrary property access:
50
+ // require('config').configuration.logLevel = 'warn' will work
51
+ // eslint-disable-next-line no-constructor-return
52
+ return new Proxy(this, {
53
+ get(target, prop) {
54
+ // lazy loading of cds.env
55
+ // if we don't do this, configuration will load cds.env whenever it is
56
+ // first imported anywhere (even by proxy from, say, cli.js).
57
+ // So we don't get to modify cds.env before that, which is important
58
+ // in cds-build.js.
59
+ // FIXME: revisit. This is horrible.
60
+ if (target.values === undefined) target.init()
61
+ return target[prop] ?? target.proxy[prop]
62
+ },
63
+ set(target, p, v) {
64
+ if (target.values === undefined) target.init()
65
+
66
+ // this.value, this.proxy etc should not be forwarded to the wrapped values
67
+ if (target[p]) {
68
+ target[p] = v
69
+ } else {
70
+ target.proxy[p] = v
71
+ }
72
+ return true
73
+ }
74
+ })
75
+ }
76
+
77
+ /**
78
+ * @param {string} key - The key to set.
79
+ * @param {any} value - The value to set
80
+ */
81
+ setOne (key, value) {
82
+ this.proxy[key] = value
83
+ }
84
+
85
+ /**
86
+ * @param {object} props - The properties to set.
87
+ */
88
+ setMany (props) {
89
+ for (const [k,v] of Object.entries(props)) {
90
+ this.proxy[k] = v
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Resets the config value and sets all its values from another passed
96
+ * config object. This allows to keep the reference to the same object.
97
+ * @param {Config} config - Another config object to set all config entries from.
98
+ */
99
+ setFrom (config) {
100
+ this.values = camelSnakeHybrid({})
101
+ this.setMany(config.values)
102
+ }
103
+
104
+ clone () {
105
+ const res = new Config()
106
+ res.init()
107
+ res.setMany(this.values)
108
+ return res
109
+ }
110
+ }
111
+
112
+ module.exports = {
113
+ camelSnakeHybrid,
114
+ /** @type {import('./typedefs').config.Configuration} */
115
+ // @ts-ignore
116
+ configuration: new Config()
117
+ }
package/lib/file.js CHANGED
@@ -6,7 +6,9 @@ const { readFileSync } = require('fs')
6
6
  const { printEnum, propertyToInlineEnumName, stringifyEnumImplementation } = require('./components/enum')
7
7
  const { normalise } = require('./components/identifier')
8
8
  const { empty } = require('./components/typescript')
9
+ const { proxyAccessFunction } = require('./components/javascript')
9
10
  const { createObjectOf } = require('./components/wrappers')
11
+ const { configuration } = require('./config')
10
12
 
11
13
  const AUTO_GEN_NOTE = '// This is an automatically generated file. Please do not change its contents manually!'
12
14
 
@@ -140,50 +142,64 @@ class SourceFile extends File {
140
142
  this.inflections = []
141
143
  /** @type {{ buffer: Buffer, names: string[]}} */
142
144
  this.services = { buffer: new Buffer(), names: [] }
145
+ /** @type {Record<string,string[]>} */
146
+ this.entityProxies = {}
143
147
  }
144
148
 
145
149
  /**
146
150
  * Stringifies a lambda expression.
147
151
  * @param {object} options - options
148
152
  * @param {string} options.name - name of the lambda
149
- * @param {[string, string][]} [options.parameters] - list of parameters, passed as [name, type] pairs
153
+ * @param {import('./typedefs').visitor.ParamInfo[]} [options.parameters] - list of parameters, passed as [name, modifier, type, doc] pairs
150
154
  * @param {string} [options.returns] - the return type of the function
151
155
  * @param {'action' | 'function'} options.kind - kind of the lambda
152
156
  * @param {string} [options.initialiser] - the initialiser expression
153
157
  * @param {boolean} [options.isStatic] - whether the lambda is static
158
+ * @param {{positional?: boolean, named?: boolean}} [options.callStyles] - whether to generate positional and/or named call styles
159
+ * @param {string[]?} [options.doc] - documentation for the operation
154
160
  * @returns {string} the stringified lambda
155
161
  * @example
156
162
  * ```js
157
163
  * // note: these samples are actually simplified! See below.
158
- * stringifyLambda({parameters: [['p','T']]}) // f: { (p: T): any, ... }
159
- * stringifyLambda({name: 'f', parameters: [['p','T']]}) // f: { (p: T) => any, ... }
160
- * stringifyLambda({name: 'f', parameters: [['p','T']], returns: 'number'}) // f: { (p: T) => number, ... }
161
- * stringifyLambda({name: 'f', parameters: [['p','T']], returns: 'number', initialiser: '_ => 42'}) // f: { (p: T): string = _ => 42, ... }
164
+ * stringifyLambda({parameters: [['p','','T']]}) // f: { (p: T): any, ... }
165
+ * stringifyLambda({name: 'f', parameters: [{name:'p',type:'T'}]}) // f: { (p: T) => any, ... }
166
+ * stringifyLambda({name: 'f', parameters: [{name:'p',modifier:'?',type:'T',doc:'/** doc *\/'}], returns: 'number'}) // /** doc *\/f?: { (p: T) => number, ... }
167
+ * stringifyLambda({name: 'f', parameters: [{name:'p',type:'T'}], returns: 'number', initialiser: '_ => 42'}) // f?: { (p: T): string = _ => 42, ... }
162
168
  * ```
163
169
  *
164
- * The generated string will not be just the signature of the function. Instead, it will be an object offering a callable signature.
170
+ * The generated string will not be just the signature of the function. Instead, it will be an object offering callable signature(s).
165
171
  * On top of that, it will also expose a property `__parameters`, which is an object reflecting the functions parameters.
166
172
  * The reason for this is that the CDS runtime actually treats the function parameters as a named object. This can not be rectified via
167
173
  * type magic, as parameter names do not exist on type level. So we can not use these names to reuse them as object properties.
168
174
  * Instead, we generate this utility object for the runtime to use:
169
175
  * @example
170
176
  * ```js
171
- * stringifyLambda({name: 'f', parameters: [['p','T']], returns: 'number'}) // { (p: T): number, __parameters: { p: T } }
177
+ * stringifyLambda({name: 'f', parameters: [{name:'p',type:'T'}], returns: 'number'}) // { (p: T): number, __parameters: { p: T } }
172
178
  * ```
173
179
  */
174
- static stringifyLambda({name, parameters=[], returns='any', initialiser, isStatic=false, kind}) {
175
- const parameterTypes = parameters.map(([n, t]) => `${normalise(n)}: ${t}`).join(', ')
180
+ static stringifyLambda({name, parameters=[], returns='any', kind, initialiser, isStatic=false, callStyles={positional:true, named:true}, doc}) {
181
+ let docStr = doc?.length ? doc.join('\n')+'\n' : ''
182
+ const parameterTypes = parameters.map(({name, modifier, type, doc}) => `${doc?'\n'+doc:''}${normalise(name)}${modifier}: ${type}`).join(', ')
176
183
  const parameterTypeAsObject = parameterTypes.length
177
184
  ? createObjectOf(parameterTypes)
178
185
  : empty
179
- const callableSignature = `(${parameterTypes}): ${returns}`
186
+ const callableSignatures = []
187
+ if (callStyles.positional) {
188
+ const paramTypesPositional = parameters.map(({name, type, doc}) => `${doc?'\n'+doc:''}${normalise(name)}: ${type}`).join(', ') // must not include ? modifiers
189
+ callableSignatures.push(`// positional\n${docStr}(${paramTypesPositional}): ${returns}`) // docs shows up on action consumer side: `.action(...)`
190
+ }
191
+ if (callStyles.named) {
192
+ const parameterNames = createObjectOf(parameters.map(({name}) => normalise(name)).join(', '))
193
+ callableSignatures.push(`// named\n${docStr}(${parameterNames}: ${parameterTypeAsObject}): ${returns}`)
194
+ }
195
+ if (callableSignatures.length === 0) throw new Error('At least one call style must be specified')
180
196
  let prefix = name ? `${normalise(name)}: `: ''
181
197
  if (prefix && isStatic) {
182
198
  prefix = `static ${prefix}`
183
199
  }
184
200
  const kindDef = kind ? `, kind: '${kind}'` : ''
185
201
  const suffix = initialiser ? ` = ${initialiser}` : ''
186
- const lambda = `{ ${callableSignature}, __parameters: ${parameterTypeAsObject}, __returns: ${returns}${kindDef}}`
202
+ const lambda = `{\n${callableSignatures.join('\n')}, \n// metadata (do not use)\n__parameters: ${parameterTypeAsObject}, __returns: ${returns}${kindDef}}`
187
203
  return prefix + lambda + suffix
188
204
  }
189
205
 
@@ -203,13 +219,16 @@ class SourceFile extends File {
203
219
  /**
204
220
  * Adds a function definition in form of a arrow function to the file.
205
221
  * @param {string} name - name of the function
206
- * @param {[string, string][]} parameters - list of parameters, passed as [name, type] pairs
222
+ * @param {import('./typedefs').visitor.ParamInfo[]} parameters - list of parameters, passed as [name, modifier, type] tuple
207
223
  * @param {string} returns - the return type of the function
208
224
  * @param {'function' | 'action'} kind - kind of the node
225
+ * @param {string[]} doc - documentation for the function
226
+ * @param {{positional?: boolean, named?: boolean}} callStyles - how the operation can be called
209
227
  */
210
- addOperation(name, parameters, returns, kind) {
211
- //this.operations.buffer.add("// operation")
212
- this.operations.buffer.add(`export declare const ${SourceFile.stringifyLambda({name, parameters, returns, kind})};`)
228
+ addOperation(name, parameters, returns, kind, doc, callStyles) {
229
+ // this.operations.buffer.add(`// ${kind}`)
230
+ if (doc) this.operations.buffer.add(doc.join('\n')) // docs shows up on action provider side: `.on(action,...)`
231
+ this.operations.buffer.add(`export declare const ${SourceFile.stringifyLambda({name, parameters, returns, kind, doc, callStyles})};`)
213
232
  this.operations.names.push(name)
214
233
  }
215
234
 
@@ -239,10 +258,11 @@ class SourceFile extends File {
239
258
  * @param {string} fq - fully qualified name of the enum (entity name within CSN)
240
259
  * @param {string} name - local name of the enum
241
260
  * @param {[string, string][]} kvs - list of key-value pairs
261
+ * @param {string[]} doc - the enum docs
242
262
  */
243
- addEnum(fq, name, kvs) {
263
+ addEnum(fq, name, kvs, doc) {
244
264
  this.enums.data.push({ name, fq, kvs })
245
- printEnum(this.enums.buffer, name, kvs)
265
+ printEnum(this.enums.buffer, name, kvs, {}, doc)
246
266
  }
247
267
 
248
268
  /**
@@ -251,6 +271,7 @@ class SourceFile extends File {
251
271
  * @param {string} entityFqName - name of the entity the enum is attached to with namespace
252
272
  * @param {string} propertyName - property to which the enum is attached.
253
273
  * @param {[string, string][]} kvs - list of key-value pairs
274
+ * @param {string[]} doc - the enum docs
254
275
  * If given, the enum is considered to be an inline definition of an enum.
255
276
  * If not, it is considered to be regular, named enum.
256
277
  * @example
@@ -274,14 +295,16 @@ class SourceFile extends File {
274
295
  * }
275
296
  * ```
276
297
  */
277
- addInlineEnum(entityCleanName, entityFqName, propertyName, kvs) {
298
+ addInlineEnum(entityCleanName, entityFqName, propertyName, kvs, doc=[]) {
278
299
  this.enums.data.push({
279
300
  name: `${entityCleanName}.${propertyName}`,
280
301
  property: propertyName,
281
302
  kvs,
282
303
  fq: `${entityCleanName}.${propertyName}`
283
304
  })
284
- printEnum(this.inlineEnums.buffer, propertyToInlineEnumName(entityCleanName, propertyName), kvs, {export: false})
305
+ const entityProxy = this.entityProxies[entityCleanName] ?? (this.entityProxies[entityCleanName] = [])
306
+ entityProxy.push(propertyName)
307
+ printEnum(this.inlineEnums.buffer, propertyToInlineEnumName(entityCleanName, propertyName), kvs, {export: false}, doc)
285
308
  }
286
309
 
287
310
  /**
@@ -363,11 +386,16 @@ class SourceFile extends File {
363
386
  */
364
387
  getImports() {
365
388
  const buffer = new Buffer()
389
+ if (this.services.names.length) {
390
+ // currently only needed to extend cds.Service and would trigger unused-variable-errors in strict configs
391
+ buffer.add('import cds from \'@sap/cds\'') // TODO should go to visitor#printService, but can't express this as Path
392
+ }
366
393
  for (const imp of Object.values(this.imports)) {
367
394
  if (!imp.isCwd(this.path.asDirectory())) {
368
395
  buffer.add(`import * as ${imp.asIdentifier()} from '${imp.asDirectory({relative: this.path.asDirectory()})}';`)
369
396
  }
370
397
  }
398
+ buffer.add('') // empty line after imports
371
399
  return buffer
372
400
  }
373
401
 
@@ -394,31 +422,92 @@ class SourceFile extends File {
394
422
  namespaces.join() // needs to be after classes for possible declaration merging
395
423
  ].filter(Boolean).join('\n')
396
424
  }
425
+ #getEntityProxyFunctionExport() {
426
+ return `module.exports.createEntityProxy = ${proxyAccessFunction}`
427
+ }
428
+ /**
429
+ * Returns boilerplate code for `index.js` files
430
+ * - `useEntitiesProxy = true` -> import `createEntityProxy` function for entity proxy
431
+ * - `useEntitiesProxy = false` -> retrieve entities via `cds.entities(namespace)`
432
+ * @returns {string[]}
433
+ */
434
+ #getJSExportBoilerplate() {
435
+ const namespace = this.path.asNamespace()
397
436
 
437
+ const boilerplate = [AUTO_GEN_NOTE]
438
+ if (configuration.useEntitiesProxy) {
439
+ if (namespace === '_') {
440
+ boilerplate.push('const cds = require(\'@sap/cds\')', this.#getEntityProxyFunctionExport())
441
+ } else {
442
+ boilerplate.push(`const { createEntityProxy } = require('${new Path(['_']).asDirectory({relative: this.path.asDirectory()})}')`)
443
+ }
444
+ } else {
445
+ boilerplate.push(
446
+ 'const cds = require(\'@sap/cds\')',
447
+ `const csn = cds.entities('${namespace}')`
448
+ )
449
+ }
450
+ return boilerplate
451
+ }
452
+ /**
453
+ * Returns RHS for entity `module.exports` assignments
454
+ * - `useEntitiesProxy = true` -> use function calls to create `Proxy` objects
455
+ * - `useEntitiesProxy = false` -> access entity from CSN directly
456
+ * @param {string} singular - singular name of entity
457
+ * @param {string} original - original name of entity
458
+ * @returns {{singularRhs: string, pluralRhs: string}}
459
+ */
460
+ #getEntityExportsRhs(singular, original) {
461
+ if (configuration.useEntitiesProxy) {
462
+ const namespace = this.path.asNamespace()
463
+ // determine the custom properties for the proxy function call
464
+ const customProps = this.entityProxies[singular] ?? []
465
+ let customPropsStr = customProps.length ? `, customProps: ${JSON.stringify(customProps)}` : ''
466
+
467
+ return {
468
+ singularRhs: `createEntityProxy(['${namespace}', '${original}'], { target: { is_singular: true }${customPropsStr} })`,
469
+ pluralRhs: `createEntityProxy(['${namespace}', '${original}'])`,
470
+ }
471
+ } else {
472
+ return {
473
+ singularRhs: `{ is_singular: true, __proto__: csn.${original} }`,
474
+ pluralRhs: `csn.${original}`
475
+ }
476
+ }
477
+ }
398
478
  toJSExports() {
399
- return [AUTO_GEN_NOTE, 'const cds = require(\'@sap/cds\')', `const csn = cds.entities('${this.path.asNamespace()}')`] // boilerplate
479
+ return this.#getJSExportBoilerplate() // boilerplate
400
480
  .concat(
401
481
  // FIXME: move stringification of service into own module
402
- this.services.names.map(name => `module.exports = { name: '${name}' }`)) // there should be only one
482
+ this.services.names.flatMap(name => {
483
+ const nameSimple = name.split('.').pop()
484
+ return [
485
+ '// service',
486
+ `const ${nameSimple} = { name: '${name}' }`,
487
+ `module.exports = ${nameSimple}`, // there should be only one and must be the first
488
+ `module.exports.${nameSimple} = ${nameSimple}`
489
+ ]
490
+ })
491
+ )
403
492
  .concat(this.inflections
404
493
  // sorting the entries based on the number of dots in their singular.
405
494
  // that makes sure we have defined all parent namespaces before adding subclasses to them e.g.:
406
495
  // "module.exports.Books" is defined before "module.exports.Books.text"
407
496
  .sort(([a], [b]) => a.split('.').length - b.split('.').length)
408
497
  .flatMap(([singular, plural, original]) => {
409
- const exports = [`module.exports.${singular} = { is_singular: true, __proto__: csn.${original} }`]
498
+ const { singularRhs, pluralRhs } = this.#getEntityExportsRhs(singular, original)
499
+
500
+ const exports = [`// ${original}`, `module.exports.${singular} = ${singularRhs}`]
410
501
  if (!/Array<.*>/.test(plural) && plural !== original) {
411
502
  // FIXME: this is a hack to support CDS types that will produce "Array<MyType>" as plural, which we do not want as export in the index.js files
412
- exports.push(`module.exports.${plural} = csn.${original}`)
503
+ exports.push(`module.exports.${plural} = ${pluralRhs}`)
413
504
  }
414
505
  // FIXME: we currently produce at most 3 entries.
415
506
  // This could be an issue when the user re-used the original name in a @singular/@plural annotation.
416
507
  // Seems unlikely, but we have to eliminate the original entry if users start running into this.
417
508
  if (singular !== original) {
418
509
  // do not do the is_singular spiel if the original name is used for the plural
419
- const rhs = plural === original
420
- ? `csn.${original}`
421
- : `{ is_singular: true, __proto__: csn.${original} }`
510
+ const rhs = plural === original ? pluralRhs : singularRhs
422
511
  exports.push(`module.exports.${original} = ${rhs}`)
423
512
  }
424
513
  return exports
@@ -3,7 +3,7 @@
3
3
  const util = require('../util')
4
4
  // eslint-disable-next-line no-unused-vars
5
5
  const { Buffer, SourceFile, Path, Library } = require('../file')
6
- const { deepRequire, createToManyAssociation, createToOneAssociation, createArrayOf, createCompositionOfMany, createCompositionOfOne } = require('../components/wrappers')
6
+ const { deepRequire, createToManyAssociation, createToOneAssociation, createArrayOf, createCompositionOfMany, createCompositionOfOne, createKey } = require('../components/wrappers')
7
7
  const { StructuredInlineDeclarationResolver } = require('../components/inline')
8
8
  const { isInlineEnumType, propertyToInlineEnumName } = require('../components/enum')
9
9
  const { isReferenceType } = require('../components/reference')
@@ -13,6 +13,7 @@ const { BuiltinResolver } = require('./builtin')
13
13
  const { LOG } = require('../logging')
14
14
  const { last } = require('../components/identifier')
15
15
  const { getPropertyModifiers } = require('../components/property')
16
+ const { configuration } = require('../config')
16
17
 
17
18
  /** @typedef {import('../visitor').Visitor} Visitor */
18
19
  /** @typedef {import('../typedefs').resolver.CSN} CSN */
@@ -30,7 +31,7 @@ class Resolver {
30
31
  this.visitor = visitor
31
32
 
32
33
  /** @type {BuiltinResolver} */
33
- this.builtinResolver = new BuiltinResolver(visitor.options)
34
+ this.builtinResolver = new BuiltinResolver({ IEEE754Compatible: configuration.IEEE754Compatible })
34
35
 
35
36
  /** @type {Library[]} */
36
37
  this.libraries = [new Library(require.resolve('../../library/cds.hana.ts'))]
@@ -58,6 +59,14 @@ class Resolver {
58
59
  return this.existsInCsn(fq) || Boolean(this.builtinResolver.resolveBuiltin(fq))
59
60
  }
60
61
 
62
+ /**
63
+ * @param {EntityCSN} type - a CSN type
64
+ * @returns {boolean} whether the type is configured to be optional
65
+ */
66
+ isOptional(type) {
67
+ return !type.notNull
68
+ }
69
+
61
70
  /**
62
71
  * Returns all libraries that have been referenced at least once.
63
72
  * @returns {Library[]}
@@ -150,8 +159,8 @@ class Resolver {
150
159
  if (parts.length <= 1) return []
151
160
 
152
161
  /**
153
- * @param {string} property
154
- * @param {import('../typedefs').resolver.EntityCSN} entity
162
+ * @param {string} property - the property to check
163
+ * @param {import('../typedefs').resolver.EntityCSN} entity - the entity to check the property against
155
164
  */
156
165
  const isPropertyOf = (property, entity) => property && Object.hasOwn(entity?.elements ?? {}, property)
157
166
 
@@ -376,6 +385,10 @@ class Resolver {
376
385
  plural: typeName
377
386
  }
378
387
 
388
+ if (element.key === true) {
389
+ typeName = createKey(typeName)
390
+ }
391
+
379
392
  // FIXME: typeName could probably just become part of typeInfo
380
393
  return { typeName, typeInfo }
381
394
  }
package/lib/typedefs.d.ts CHANGED
@@ -8,6 +8,7 @@ export module resolver {
8
8
 
9
9
  export type EntityCSN = {
10
10
  actions?: OperationCSN[],
11
+ operations?: OperationCSN[],
11
12
  cardinality?: { max?: '*' | string }
12
13
  compositions?: { target: string }[]
13
14
  doc?: string,
@@ -98,50 +99,76 @@ export module resolver {
98
99
 
99
100
  export module util {
100
101
  export type Annotations = {
101
- name?: string,
102
+ name: string,
102
103
  '@singular'?: string,
103
104
  '@plural'?: string
104
105
  }
106
+ }
107
+
108
+ export module visitor {
109
+ export type Inflection = {
110
+ typeName?: string,
111
+ singular: string,
112
+ plural: string
113
+ }
105
114
 
106
- export type CommandLineFlags = {
107
- desc: string,
108
- default?: any
115
+ export type Context = {
116
+ entity: string
109
117
  }
110
118
 
111
- export type ParsedFlag = {
112
- positional: string[],
113
- named: { [key: string]: any }
119
+ export type ParamInfo = {
120
+ name: string,
121
+ modifier: '' | '?',
122
+ type: string,
123
+ doc?: string
114
124
  }
115
125
  }
116
126
 
117
- export module visitor {
118
- export type CompileParameters = {
127
+ export module config {
128
+ export module cli {
129
+ export type CLIFlags = 'version' | 'help'
130
+ export type ParameterSchema = {
131
+ [key: string]: {
132
+ desc: string,
133
+ allowed?: string[],
134
+ allowedHint?: string,
135
+ type?: 'string' | 'boolean' | 'number',
136
+ default?: string,
137
+ defaultHint?: string,
138
+ postprocess?: (value: string) => any,
139
+ camel?: string,
140
+ snake?: string
141
+ }
142
+ }
143
+
144
+ export type ParsedParameters = {
145
+ positional: string[],
146
+ named: { [key: keyof RuntimeParameters]: {
147
+ value: any,
148
+ isDefault: boolean,
149
+ } }
150
+ }
151
+ }
152
+
153
+ export type Configuration = {
119
154
  outputDirectory: string,
120
155
  logLevel: number,
156
+ /**
157
+ * `useEntitiesProxy = true` will wrap the `module.exports.<entityName>` in `Proxy` objects
158
+ */
159
+ useEntitiesProxy: boolean,
121
160
  jsConfigPath?: string,
122
- inlineDeclarations: 'flat' | 'structured',
123
- propertiesOptional: boolean,
124
- IEEE754Compatible: boolean,
125
- }
126
-
127
- export type VisitorOptions = {
128
- /** `propertiesOptional = true` -> all properties are generated as optional ?:. (standard CAP behaviour, where properties be unavailable) */
129
- propertiesOptional: boolean,
130
161
  /**
131
162
  * `inlineDeclarations = 'structured'` -> @see {@link inline.StructuredInlineDeclarationResolver}
132
163
  * `inlineDeclarations = 'flat'` -> @see {@link inline.FlatInlineDeclarationResolver}
133
164
  */
134
165
  inlineDeclarations: 'flat' | 'structured',
135
- }
136
-
137
- export type Inflection = {
138
- typeName?: string,
139
- singular: string,
140
- plural: string
141
- }
142
-
143
- export type Context = {
144
- entity: string
166
+ /** `propertiesOptional = true` -> all properties are generated as optional ?:. (standard CAP behaviour, where properties be unavailable) */
167
+ propertiesOptional: boolean,
168
+ /**
169
+ * `IEEE754Compatible = true` -> any cds.Decimal will become `number | string`
170
+ */
171
+ IEEE754Compatible: boolean
145
172
  }
146
173
  }
147
174