@cap-js/cds-typer 0.20.2 → 0.21.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 +21 -2
- package/cds-plugin.js +126 -0
- package/lib/cli.js +7 -1
- package/lib/compile.js +1 -1
- package/lib/components/resolver.js +61 -46
- package/lib/file.js +11 -9
- package/lib/visitor.js +13 -8
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,26 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
-
## Version 0.
|
|
7
|
+
## Version 0.22.0 - TBD
|
|
8
|
+
|
|
9
|
+
## Version 0.21.1 - 2024-06-03
|
|
10
|
+
### Fixed
|
|
11
|
+
- Added missing _cds-plugin.js_ to exported files to properly enable calling `cds build --for typescript`
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Version 0.21.0 - 2024-05-31
|
|
15
|
+
### Added
|
|
16
|
+
- Added `IEEE754Compatible` flag which, when set to `true`, generates decimal fields as `(number | string)` instead of `number`. This flag will be removed in the long run
|
|
17
|
+
- Added plugin to `cds build` TypeScript projects. Can be explicitly called using `cds build --for typescript`
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Types representing CDS events are now only `declare`d to avoid having to make their properties optional
|
|
21
|
+
- Singular forms in generated _index.js_ files now contain a `.is_singular` property as marker for distinguished handling of singular and plural in the runtime
|
|
22
|
+
- Parameters passed to the CLI now take precedence over configuration contained in the `typer` section of `cds.env`
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Entities ending with an "s" are no longer incorrectly truncated within `extends`-clauses
|
|
26
|
+
- Entity names prefixed with their own namespace (e.g. `Name.Name`, `Name.NameAttachments`) are not stripped of their name prefix
|
|
8
27
|
|
|
9
28
|
## Version 0.20.2 - 2024-04-29
|
|
10
29
|
### Fixed
|
|
@@ -17,7 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
17
36
|
## Version 0.20.0 - 2024-04-23
|
|
18
37
|
### Added
|
|
19
38
|
- Types for actions and functions now expose a `.kind` property which holds the string `'function'` or `'action'` respectively
|
|
20
|
-
- Added the CdsDate
|
|
39
|
+
- Added the `CdsDate`, `CdsDateTime`, `CdsTime`, `CdsTimestamp` types, which are each represented as a `string`.
|
|
21
40
|
- Plural types can now also contain an optional numeric `$count` property
|
|
22
41
|
|
|
23
42
|
### Changed
|
package/cds-plugin.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const { readdir, stat } = require('node:fs/promises')
|
|
2
|
+
const { normalize } = require('node:path')
|
|
3
|
+
const cds = require('@sap/cds')
|
|
4
|
+
const util = require('util')
|
|
5
|
+
const exec = util.promisify(require('child_process').exec)
|
|
6
|
+
const typer = require('./lib/compile')
|
|
7
|
+
|
|
8
|
+
const { fs, path } = cds.utils
|
|
9
|
+
const DEBUG = cds.debug('cli|build')
|
|
10
|
+
const BUILD_CONFIG = 'tsconfig.cdsbuild.json'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if a tsconfig file exists.
|
|
14
|
+
*/
|
|
15
|
+
const tsConfigExists = () => fs.existsSync('tsconfig.json')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if separate tsconfig file that is used for building the project.
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
const buildConfigExists = () => fs.existsSync(BUILD_CONFIG)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} dir The directory to remove.
|
|
25
|
+
*/
|
|
26
|
+
const rmDirIfExists = dir => {
|
|
27
|
+
try { fs.rmSync(dir, { recursive: true }) } catch { /* ignore */ }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Remove files with given extensions from a directory recursively.
|
|
32
|
+
* @param {string} dir The directory to start from.
|
|
33
|
+
* @param {string[]} exts The extensions to remove.
|
|
34
|
+
* @returns {Promise<void>}
|
|
35
|
+
*/
|
|
36
|
+
const rmFiles = async (dir, exts) => fs.existsSync(dir)
|
|
37
|
+
? Promise.all(
|
|
38
|
+
(await readdir(dir))
|
|
39
|
+
.map(async file => {
|
|
40
|
+
const filePath = path.join(dir, file)
|
|
41
|
+
if ((await stat(filePath)).isDirectory()) {
|
|
42
|
+
return rmFiles(filePath, exts)
|
|
43
|
+
} else if (exts.some(ext => file.endsWith(ext))) {
|
|
44
|
+
fs.unlinkSync(filePath)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
)
|
|
48
|
+
: undefined
|
|
49
|
+
|
|
50
|
+
// requires @sap/cds-dk version >= 7.5.0
|
|
51
|
+
cds.build?.register?.('typescript', class extends cds.build.Plugin {
|
|
52
|
+
static taskDefaults = { src: '.' }
|
|
53
|
+
static hasTask() { return tsConfigExists() }
|
|
54
|
+
|
|
55
|
+
// lower priority than the nodejs task
|
|
56
|
+
get priority() { return -1 }
|
|
57
|
+
|
|
58
|
+
get #appFolder () { return cds?.env?.folders?.app ?? 'app' }
|
|
59
|
+
|
|
60
|
+
get #modelDirectoryName () {
|
|
61
|
+
try {
|
|
62
|
+
// expected format: { '#cds-models/*': [ './@cds-models/*/index.ts' ] }
|
|
63
|
+
// ^^^^^^^^^^^^^^^
|
|
64
|
+
// relevant part - may be changed by user
|
|
65
|
+
const config = JSON.parse(fs.readFileSync ('tsconfig.json', 'utf8'))
|
|
66
|
+
const alias = config.compilerOptions.paths['#cds-models/*'][0]
|
|
67
|
+
const directory = alias.match(/(?:\.\/)?(.*)\/\*\/index\.ts/)[1]
|
|
68
|
+
return normalize(directory) // could contain forward slashes in tsconfig.json
|
|
69
|
+
} catch {
|
|
70
|
+
DEBUG?.('tsconfig.json not found, not parsable, or inconclusive. Using default model directory name')
|
|
71
|
+
}
|
|
72
|
+
return '@cds-models'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
init() {
|
|
76
|
+
this.task.dest = path.join(cds.root, cds.env.build.target, cds.env.folders.srv)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async #runCdsTyper () {
|
|
80
|
+
DEBUG?.('running cds-typer')
|
|
81
|
+
await typer.compileFromFile('*', { outputDirectory: this.#modelDirectoryName })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async #buildWithConfig () {
|
|
85
|
+
// possibly referencing their tsconfig.json via "extends", specifying the "compilerOptions.outDir" and
|
|
86
|
+
// manually adding irrelevant folders (read: gen/ and app/) to the "exclude" array.
|
|
87
|
+
DEBUG?.(`building with config ${BUILD_CONFIG}`)
|
|
88
|
+
return exec(`npx tsc --project ${BUILD_CONFIG}`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async #buildWithoutConfig () {
|
|
92
|
+
DEBUG?.('building without config')
|
|
93
|
+
// this will include gen/ that was created by the nodejs task
|
|
94
|
+
// _within_ the project directory. So we need to remove it afterwards.
|
|
95
|
+
await exec(`npx tsc --outDir ${this.task.dest}`)
|
|
96
|
+
rmDirIfExists(path.join(this.task.dest, cds.env.build.target))
|
|
97
|
+
rmDirIfExists(path.join(this.task.dest, this.#appFolder))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async #copyCleanModel (buildDirCdsModels) {
|
|
101
|
+
// copy models again, to revert transpilation thereof.
|
|
102
|
+
// We only need the index.js files in un-transpiled form.
|
|
103
|
+
await this.copy(this.#modelDirectoryName).to(buildDirCdsModels)
|
|
104
|
+
await rmFiles(buildDirCdsModels, ['.ts'])
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async build() {
|
|
108
|
+
await this.#runCdsTyper()
|
|
109
|
+
const buildDirCdsModels = path.join(this.task.dest, this.#modelDirectoryName)
|
|
110
|
+
// remove the js files generated by the nodejs buildtask,
|
|
111
|
+
// leaving only json, cds, and other static files
|
|
112
|
+
await rmFiles(this.task.dest, ['.js', '.ts'])
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
await (buildConfigExists()
|
|
116
|
+
? this.#buildWithConfig()
|
|
117
|
+
: this.#buildWithoutConfig()
|
|
118
|
+
)
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw error.stdout
|
|
121
|
+
? new Error(error.stdout)
|
|
122
|
+
: error
|
|
123
|
+
}
|
|
124
|
+
this.#copyCleanModel(buildDirCdsModels)
|
|
125
|
+
}
|
|
126
|
+
})
|
package/lib/cli.js
CHANGED
|
@@ -41,6 +41,11 @@ const flags = {
|
|
|
41
41
|
desc: `If set to true, properties in entities are${EOL}always generated as optional (a?: T).`,
|
|
42
42
|
allowed: ['true', 'false'],
|
|
43
43
|
default: 'true'
|
|
44
|
+
},
|
|
45
|
+
IEEE754Compatible: {
|
|
46
|
+
desc: `If set to true, floating point properties are generated${EOL}as IEEE754 compatible '(number | string)' instead of 'number'.`,
|
|
47
|
+
allowed: ['true', 'false'],
|
|
48
|
+
default: 'false'
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
|
|
@@ -93,7 +98,8 @@ const main = async args => {
|
|
|
93
98
|
logLevel: Levels[args.named.logLevel] ?? args.named.logLevel,
|
|
94
99
|
jsConfigPath: args.named.jsConfigPath,
|
|
95
100
|
inlineDeclarations: args.named.inlineDeclarations,
|
|
96
|
-
propertiesOptional: args.named.propertiesOptional === 'true'
|
|
101
|
+
propertiesOptional: args.named.propertiesOptional === 'true',
|
|
102
|
+
IEEE754Compatible: args.named.IEEE754Compatible === 'true'
|
|
97
103
|
})
|
|
98
104
|
}
|
|
99
105
|
|
package/lib/compile.js
CHANGED
|
@@ -45,7 +45,7 @@ const writeJsConfig = (path, logger) => {
|
|
|
45
45
|
*/
|
|
46
46
|
const compileFromCSN = async (csn, parameters) => {
|
|
47
47
|
const envSettings = cds.env?.typer ?? {}
|
|
48
|
-
parameters = { ...
|
|
48
|
+
parameters = { ...envSettings, ...parameters }
|
|
49
49
|
const logger = new Logger()
|
|
50
50
|
logger.addFrom(parameters.logLevel)
|
|
51
51
|
if (parameters.jsConfigPath) {
|
|
@@ -39,48 +39,61 @@ const { baseDefinitions } = require('./basedefs')
|
|
|
39
39
|
* }} TypeResolveInfo
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
42
|
+
class BuiltinResolver {
|
|
43
|
+
/**
|
|
44
|
+
* Builtin types defined by CDS.
|
|
45
|
+
*/
|
|
46
|
+
#builtins = {
|
|
47
|
+
UUID: 'string',
|
|
48
|
+
String: 'string',
|
|
49
|
+
Binary: 'string',
|
|
50
|
+
LargeString: 'string',
|
|
51
|
+
LargeBinary: 'Buffer | string | {value: import("stream").Readable, $mediaContentType: string, $mediaContentDispositionFilename?: string, $mediaContentDispositionType?: string}',
|
|
52
|
+
Vector: 'string',
|
|
53
|
+
Integer: 'number',
|
|
54
|
+
UInt8: 'number',
|
|
55
|
+
Int16: 'number',
|
|
56
|
+
Int32: 'number',
|
|
57
|
+
Int64: 'number',
|
|
58
|
+
Integer64: 'number',
|
|
59
|
+
Decimal: 'number',
|
|
60
|
+
DecimalFloat: 'number',
|
|
61
|
+
Float: 'number',
|
|
62
|
+
Double: 'number',
|
|
63
|
+
Boolean: 'boolean',
|
|
64
|
+
// note: the date-related types are strings on purpose, which reflects their runtime behaviour
|
|
65
|
+
Date: '__.CdsDate', // yyyy-mm-dd
|
|
66
|
+
DateTime: '__.CdsDateTime', // yyyy-mm-dd + time + TZ (precision: seconds)
|
|
67
|
+
Time: '__.CdsTime', // hh:mm:ss
|
|
68
|
+
Timestamp: '__.CdsTimestamp', // yyy-mm-dd + time + TZ (ms precision)
|
|
69
|
+
//
|
|
70
|
+
Composition: 'Array',
|
|
71
|
+
Association: 'Array'
|
|
72
|
+
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
constructor ({ IEEE754Compatible } = {}) {
|
|
75
|
+
if (IEEE754Compatible) {
|
|
76
|
+
this.#builtins.Decimal = '(number | string)'
|
|
77
|
+
this.#builtins.DecimalFloat = '(number | string)'
|
|
78
|
+
this.#builtins.Float = '(number | string)'
|
|
79
|
+
this.#builtins.Double = '(number | string)'
|
|
80
|
+
}
|
|
81
|
+
this.#builtins = Object.freeze(this.#builtins)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {string | string[]} type name or parts of the type name split on dots
|
|
86
|
+
* @returns {string | undefined | false} if t refers to a builtin, the name of the corresponding TS type is returned.
|
|
87
|
+
* If t _looks like_ a builtin (`cds.X`), undefined is returned.
|
|
88
|
+
* If t is obviously not a builtin, false is returned.
|
|
89
|
+
*/
|
|
90
|
+
resolveBuiltin (t) {
|
|
91
|
+
if (!Array.isArray(t) && typeof t !== 'string') return false
|
|
92
|
+
const path = Array.isArray(t) ? t : t.split('.')
|
|
93
|
+
return path.length === 2 && path[0] === 'cds'
|
|
94
|
+
? this.#builtins[path[1]]
|
|
95
|
+
: false
|
|
96
|
+
}
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
class Resolver {
|
|
@@ -135,6 +148,9 @@ class Resolver {
|
|
|
135
148
|
/** @type {Visitor} */
|
|
136
149
|
this.visitor = visitor
|
|
137
150
|
|
|
151
|
+
/** @type {BuiltinResolver} */
|
|
152
|
+
this.builtinResolver = new BuiltinResolver(visitor.options)
|
|
153
|
+
|
|
138
154
|
/** @type {Library[]} */
|
|
139
155
|
this.libraries = [new Library(require.resolve('../../library/cds.hana.ts'))]
|
|
140
156
|
|
|
@@ -171,7 +187,7 @@ class Resolver {
|
|
|
171
187
|
* @returns {Untangled} untangled qualifier
|
|
172
188
|
*/
|
|
173
189
|
untangle(fq) {
|
|
174
|
-
const builtin = resolveBuiltin(fq)
|
|
190
|
+
const builtin = this.builtinResolver.resolveBuiltin(fq)
|
|
175
191
|
if (builtin) return { namespace: new Path([]), name: builtin, property: [], scope: [] }
|
|
176
192
|
|
|
177
193
|
const ns = this.resolveNamespace(fq)
|
|
@@ -318,7 +334,7 @@ class Resolver {
|
|
|
318
334
|
// don't slice off namespace if it isn't part of the inflected name.
|
|
319
335
|
// This happens when the user adds an annotation and singular4 therefore
|
|
320
336
|
// already returns an identifier without namespace. Plural has ns already sliced off.
|
|
321
|
-
if (namespace && singular.startsWith(namespace)) {
|
|
337
|
+
if (namespace && singular.startsWith(namespace+'.')) {
|
|
322
338
|
singular = singular.slice(namespace.length + 1)
|
|
323
339
|
}
|
|
324
340
|
|
|
@@ -641,7 +657,7 @@ class Resolver {
|
|
|
641
657
|
#resolveTypeName(t, into) {
|
|
642
658
|
const result = into || {}
|
|
643
659
|
const path = t.split('.')
|
|
644
|
-
const builtin = resolveBuiltin(path)
|
|
660
|
+
const builtin = this.builtinResolver.resolveBuiltin(path)
|
|
645
661
|
if (builtin === undefined) {
|
|
646
662
|
// looks like builtin, but isn't
|
|
647
663
|
throw new Error(`Can not resolve apparent builtin type '${t}' to any CDS type.`)
|
|
@@ -698,6 +714,5 @@ class Resolver {
|
|
|
698
714
|
}
|
|
699
715
|
|
|
700
716
|
module.exports = {
|
|
701
|
-
Resolver
|
|
702
|
-
resolveBuiltin
|
|
717
|
+
Resolver
|
|
703
718
|
}
|
package/lib/file.js
CHANGED
|
@@ -391,18 +391,20 @@ class SourceFile extends File {
|
|
|
391
391
|
// that makes sure we have defined all parent namespaces before adding subclasses to them e.g.:
|
|
392
392
|
// "module.exports.Books" is defined before "module.exports.Books.text"
|
|
393
393
|
.sort(([a], [b]) => a.split('.').length - b.split('.').length)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
/Array<.*>/.test(plural) ? undefined : `module.exports.${plural} = csn.${original}`,
|
|
394
|
+
.flatMap(([singular, plural, original]) => {
|
|
395
|
+
const exports = [`module.exports.${singular} = { is_singular: true, __proto__: csn.${original} }`]
|
|
396
|
+
if (!/Array<.*>/.test(plural) && plural !== original) {
|
|
397
|
+
// 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
|
|
398
|
+
exports.push(`module.exports.${plural} = csn.${original}`)
|
|
399
|
+
}
|
|
401
400
|
// FIXME: we currently produce at most 3 entries.
|
|
402
401
|
// This could be an issue when the user re-used the original name in a @singular/@plural annotation.
|
|
403
402
|
// Seems unlikely, but we have to eliminate the original entry if users start running into this.
|
|
404
|
-
|
|
405
|
-
|
|
403
|
+
if (singular !== original) {
|
|
404
|
+
exports.push(`module.exports.${original} = { is_singular: true, __proto__: csn.${original} }`)
|
|
405
|
+
}
|
|
406
|
+
return exports
|
|
407
|
+
})
|
|
406
408
|
) // singular -> plural aliases
|
|
407
409
|
.concat(['// events'])
|
|
408
410
|
.concat(this.events.fqs.map(({fq, name}) => `module.exports.${name} = '${fq}'`))
|
package/lib/visitor.js
CHANGED
|
@@ -6,7 +6,7 @@ const { amendCSN, isView, isUnresolved, propagateForeignKeys, isDraftEnabled, is
|
|
|
6
6
|
// eslint-disable-next-line no-unused-vars
|
|
7
7
|
const { SourceFile, FileRepository, Buffer } = require('./file')
|
|
8
8
|
const { FlatInlineDeclarationResolver, StructuredInlineDeclarationResolver } = require('./components/inline')
|
|
9
|
-
const { Resolver
|
|
9
|
+
const { Resolver } = require('./components/resolver')
|
|
10
10
|
const { Logger } = require('./logging')
|
|
11
11
|
const { docify } = require('./components/wrappers')
|
|
12
12
|
const { csnToEnumPairs, propertyToInlineEnumName, isInlineEnumType, stringifyEnumType } = require('./components/enum')
|
|
@@ -19,7 +19,7 @@ const { baseDefinitions } = require('./components/basedefs')
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @typedef { {
|
|
22
|
-
*
|
|
22
|
+
* outputDirectory: string,
|
|
23
23
|
* logLevel: number,
|
|
24
24
|
* jsConfigPath?: string
|
|
25
25
|
* }} CompileParameters
|
|
@@ -227,13 +227,16 @@ class Visitor {
|
|
|
227
227
|
file.addImport(namespace)
|
|
228
228
|
return [namespace, name, parent]
|
|
229
229
|
})
|
|
230
|
-
.concat([[undefined, clean,
|
|
230
|
+
.concat([[undefined, clean, name]]) // add own aspect without namespace AFTER imports were created
|
|
231
|
+
//.concat([[undefined, clean, [namespace, clean].filter(Boolean).join('.')]]) // add own aspect without namespace AFTER imports were created
|
|
231
232
|
.reverse() // reverse so that own aspect A is applied before extensions B,C: B(C(A(Entity)))
|
|
232
233
|
.reduce((wrapped, [ns, n, fq]) => {
|
|
233
234
|
// types are not inflected, so don't change those to singular
|
|
234
235
|
const refersToType = isType(this.csn.inferred.definitions[fq])
|
|
235
|
-
const ident = identAspect(refersToType
|
|
236
|
-
|
|
236
|
+
const ident = identAspect(refersToType
|
|
237
|
+
? n
|
|
238
|
+
: this.resolver.inflect({csn: this.csn.inferred.definitions[fq], plainName: n}).singular
|
|
239
|
+
)
|
|
237
240
|
return !ns || ns.isCwd(file.path.asDirectory())
|
|
238
241
|
? `${ident}(${wrapped})`
|
|
239
242
|
: `${ns.asIdentifier()}.${ident}(${wrapped})`
|
|
@@ -307,6 +310,7 @@ class Visitor {
|
|
|
307
310
|
this.#aspectify(name, target, buffer, { cleanName: singular })
|
|
308
311
|
|
|
309
312
|
buffer.add(overrideNameProperty(singular, entity.name))
|
|
313
|
+
buffer.add(`Object.defineProperty(${singular}, 'is_singular', { value: true })`)
|
|
310
314
|
|
|
311
315
|
// PLURAL
|
|
312
316
|
|
|
@@ -349,7 +353,7 @@ class Visitor {
|
|
|
349
353
|
#stringifyFunctionParamType(type, file) {
|
|
350
354
|
// if type.type is not 'cds.String', 'cds.Integer', ..., then we are actually looking
|
|
351
355
|
// at a named enum type. In that case also resolve that type name
|
|
352
|
-
if (type.enum && resolveBuiltin(type.type)) return stringifyEnumType(csnToEnumPairs(type))
|
|
356
|
+
if (type.enum && this.resolver.builtinResolver.resolveBuiltin(type.type)) return stringifyEnumType(csnToEnumPairs(type))
|
|
353
357
|
const paramType = this.resolver.resolveAndRequire(type, file)
|
|
354
358
|
return this.inlineDeclarationResolver.getPropertyDatatype(
|
|
355
359
|
paramType,
|
|
@@ -384,7 +388,7 @@ class Visitor {
|
|
|
384
388
|
// skip references to enums.
|
|
385
389
|
// "Base" enums will always have a builtin type (don't skip those).
|
|
386
390
|
// A type referencing an enum E will be considered an enum itself and have .type === E (skip).
|
|
387
|
-
if ('enum' in type && !isReferenceType(type) && resolveBuiltin(type.type)) {
|
|
391
|
+
if ('enum' in type && !isReferenceType(type) && this.resolver.builtinResolver.resolveBuiltin(type.type)) {
|
|
388
392
|
file.addEnum(name, clean, csnToEnumPairs(type))
|
|
389
393
|
} else {
|
|
390
394
|
// alias
|
|
@@ -412,7 +416,8 @@ class Visitor {
|
|
|
412
416
|
file.addEvent(clean, name)
|
|
413
417
|
const buffer = file.events.buffer
|
|
414
418
|
buffer.add('// event')
|
|
415
|
-
|
|
419
|
+
// only declare classes, as their properties are not optional, so we don't have to do awkward initialisation thereof.
|
|
420
|
+
buffer.addIndentedBlock(`export declare class ${clean} {`, function() {
|
|
416
421
|
const propOpt = this.options.propertiesOptional
|
|
417
422
|
this.options.propertiesOptional = false
|
|
418
423
|
for (const [ename, element] of Object.entries(event.elements ?? {})) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/cds-typer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "Generates .ts files for a CDS model to receive code completion in VS Code",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "github:cap-js/cds-typer",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"CHANGELOG.md",
|
|
32
32
|
"index.js",
|
|
33
33
|
"LICENSE",
|
|
34
|
-
"README.md"
|
|
34
|
+
"README.md",
|
|
35
|
+
"cds-plugin.js"
|
|
35
36
|
],
|
|
36
37
|
"types": "index.d.ts",
|
|
37
38
|
"bin": {
|