Parsing and Compiling Models
cds. compile (...)
function cds.compile (
model :
'*', 'file:<filename>' | filenames[] | // source files
CDL string | { CDL strings } // sources in memory
,
options : CSN_flavor | {
flavor? : CSN_flavor,
min? : boolean,
docs? : boolean,
locations? : boolean,
messages? : []
}
)
type CSN_flavor = 'parsed' | 'inferred'
function cds.compile (
model :
'*', 'file:<filename>' | filenames[] | // source files
CDL string | { CDL strings } // sources in memory
,
options : CSN_flavor | {
flavor? : CSN_flavor,
min? : boolean,
docs? : boolean,
locations? : boolean,
messages? : []
}
)
type CSN_flavor = 'parsed' | 'inferred'
This is the central function to compile models from files or in-memory sources to CSN. It supports different variants based on the type of the first argument model
as outlined below.
Depending on the variants, the method returns a Promise or a sync value.
Compiling .cds
files (async)
If the first argument is either a string starting with "file:"
, or an array of filenames, these files are read and compiled to a single CSN asynchronously:
let csn = await cds.compile (['db','srv','app'])
let csn = await cds.compile ('*')
let csn = await cds.compile ('file:db')
let csn = await cds.compile (['db','srv','app'])
let csn = await cds.compile ('*')
let csn = await cds.compile ('file:db')
The given filenames are resolved to effective absolute filenames using
cds.resolve
.
Single in-memory sources
If a single string, not starting with file:
is passed as first argument, it is interpreted as a CDL source string and compiled to CSN synchronously:
let csn = cds.compile (`
using {cuid} from '@sap/cds/common';
entity Foo : cuid { foo:String }
entity Bar as projection on Foo;
extend Foo with { bar:String }
`)
let csn = cds.compile (`
using {cuid} from '@sap/cds/common';
entity Foo : cuid { foo:String }
entity Bar as projection on Foo;
extend Foo with { bar:String }
`)
Note:
using from
clauses are not resolved in this usage.
Multiple in-memory sources
Finally, you can pass an object with multiple named CDL or CSN sources, which allows to also resolve using from
clauses:
let csn = cds.compile ({
'db/schema.cds': `
using {cuid} from '@sap/cds/common';
entity Foo : cuid { foo:String }
`,
'srv/services.cds': `
using {Foo} from '../db/schema';
entity Bar as projection on Foo;
extend Foo with { bar:String }
`,
'@sap/cds/common.csn': `
{"definitions":{
"cuid": { "kind": "aspect", "elements": {
"ID": { "key":true, "type": "cds.UUID" }
}}
}}
`,
})
let csn = cds.compile ({
'db/schema.cds': `
using {cuid} from '@sap/cds/common';
entity Foo : cuid { foo:String }
`,
'srv/services.cds': `
using {Foo} from '../db/schema';
entity Bar as projection on Foo;
extend Foo with { bar:String }
`,
'@sap/cds/common.csn': `
{"definitions":{
"cuid": { "kind": "aspect", "elements": {
"ID": { "key":true, "type": "cds.UUID" }
}}
}}
`,
})
Additional Options
You can pass additional options like so:
let csn = await cds.compile('*',{ min:true, docs:true })
let csn = await cds.compile('*',{ min:true, docs:true })
Option | Description |
---|---|
flavor | By default the returned CSN is in 'inferred' flavor, which is an effective model, with all aspects, includes, extensions and redirects applied and all views and projections inferred. Specify 'parsed' to only have single models parsed. |
min | Specify true to have cds.minify) applied after compiling the models. |
docs | Specify true to have the all /** ... */ doc comments captured in the CSN. |
locations | Specify true to have the all $location properties preserved in serialized CSN. |
messages | Pass an empty array to get all compiler messages collected in there. |
cds. compile (...) .to.xyz()
This is a fluent variant that combines calls to cds.compile()
with calls to one of cds.compile.to.xyz()
.
For example:
let csn = await cds.compile('*')
let sql = cds.compile.to.sql(csn)
let csn = await cds.compile('*')
let sql = cds.compile.to.sql(csn)
Can also be done like that using this fluent API:
let sql = await cds.compile('*').to.sql()
let sql = await cds.compile('*').to.sql()
As model argument can also be a CSN, we can also use it as a plain fluent API alternative, so these usages are equivalent:
let sql = cds.compile.to.sql(csn,{dialect:'sqlite'})
let sql = cds.compile(csn).to.sql({dialect:'sqlite'})
let sql = cds.compile.to.sql(csn,{dialect:'sqlite'})
let sql = cds.compile(csn).to.sql({dialect:'sqlite'})
cds. compile ...
Following are a collection of model processors which take a CSN as input and compile it to a target output.
.to .json()
function cds.compile.to.json ( options: {
indents : integer
})
function cds.compile.to.json ( options: {
indents : integer
})
Renders the given model to a formatted JSON string.
Option indents
is the indent as passed to JSON.stringify
.
.to .yaml()
Renders the given model to a formatted JSON or YAML string.
.to .edm()
.to .edmx()
Compiles and returns an OData v4 EDM, respectively EDMX model object for the passed in model, which is expected to contain at least one service definition.
Accepted options
the same as documented for cds.compile.for.odata
above, with one addition: If the model contains more than one service definition, use {service:...}
option parameter to:
- Either choose exactly one, for example,
{service:'Catalog'}
- Choose to return EDM objects for all, that means,
{service:'all'}
In case of the latter, a generator is returned that yields [ edm, {name} ]
for each service. For example, use it as follows:
// for one service
let edm = cds.compile.to.edm (csn, {service:'Catalog'})
console.log (edm)
// for one service
let edm = cds.compile.to.edm (csn, {service:'Catalog'})
console.log (edm)
// for all services
let all = cds.compile.to.edm (csn, {service:'all'})
for (let [edm,{name}] of all) console.log (name,edm)
// for all services
let all = cds.compile.to.edm (csn, {service:'all'})
for (let [edm,{name}] of all) console.log (name,edm)
.to .hdbtable()
.to .hdbcds()
Generates hdbtable/view
or hdbcds
output. Returns a generator that yields [ src, {file} ]
for each resulting .hdbtable
, .hdbview
, or .hdbcds
file. For example, use it as follows:
let all = cds.compile.to.hdbtable (csn)
for (let [src,{file}] of all)
console.log (file,src)
let all = cds.compile.to.hdbtable (csn)
for (let [src,{file}] of all)
console.log (file,src)
.to .sql()
Generates SQL DDL statements for the given model. The default returns an array with the generated statements.
Accepted options
are:
dialect
: 'plain' | 'sqlite' | 'postgres' | 'h2' → chooses the dialect to generatenames
: 'plain' | 'quoted' → allows to generate DDL using quoted namesas
: 'str' → returns a string with concatenated DDL statements.
Examples:
let ddls1 = cds.compile(csn).to.sql()
let ddls2 = cds.compile(csn).to.sql({dialect:'plain'})
let script = cds.compile(csn).to.sql({as:'str'})
let ddls1 = cds.compile(csn).to.sql()
let ddls2 = cds.compile(csn).to.sql({dialect:'plain'})
let script = cds.compile(csn).to.sql({as:'str'})
.to .cdl()
Reconstructs CDL source code for the given csn model.
.to .asyncapi()
Convert the CSN file into an AsyncAPI document:
const doc = cds.compile.to.asyncapi(csn_file)
const doc = cds.compile.to.asyncapi(csn_file)
cds. load (files)
Loads and parses a model from one or more files into a single effective model. It's essentially a shortcut to cds.compile ([...])
. In addition emits event cds 'loaded'
.
Declaration:
function cds.load (
files : filename || filenames[]
options : {...} //> as in cds.compile
)
function cds.load (
files : filename || filenames[]
options : {...} //> as in cds.compile
)
Usage examples:
// load a model from a single source
const csn = await cds.load('my-model')
// load a model from a single source
const csn = await cds.load('my-model')
// load a a model from several sources
const csn = await cds.load(['db','srv'])
// load a a model from several sources
const csn = await cds.load(['db','srv'])
The given filenames are resolved using
cds.resolve()
.Note: It's recommended to omit file suffixes to leverage automatic loading from precompiled CSN files instead of CDL sources.
cds. parse()
This is an API facade for a set of functions to parse whole CDL models, individual CQL queries, or CQL expressions. The three main methods are offered as classic functions, as well as tagged template string functions.
CDL
, cds. parse. cdl()
Parses a source string in CDL syntax and returns it as a parsed model according to the CSN spec. It's essentially a shortcut to cds.compile (..., {flavor:'parsed'})
.
Examples:
let csn = CDL`entity Foo{}`
let csn = cds.parse (`entity Foo{}`) //= shortcut to:
let csn = cds.parse.cdl (`entity Foo{}`)
let csn = CDL`entity Foo{}`
let csn = cds.parse (`entity Foo{}`) //= shortcut to:
let csn = cds.parse.cdl (`entity Foo{}`)
CQL
, cds. parse. cql()
Parses a source string in CQL syntax and returns it as a parsed query according to the [CQN spec][..cds/cqn].
Examples:
let cqn = CQL`SELECT * from Foo`
let cqn = cds.parse.cql (`SELECT * from Foo`)
let cqn = CQL`SELECT * from Foo`
let cqn = cds.parse.cql (`SELECT * from Foo`)
CXL
, cds. parse. expr()
Parses a source string in CQL expression syntax and returns it as a parsed expression according to the CQN Expressions spec.
Examples:
let cxn = CXL`foo.bar > 9`
let cxn = cds.parse.expr (`foo.bar > 9`)
//> {xpr:[ {ref:['foo', 'bar']}, '>', {val:9} ] }
let cxn = CXL`foo.bar > 9`
let cxn = cds.parse.expr (`foo.bar > 9`)
//> {xpr:[ {ref:['foo', 'bar']}, '>', {val:9} ] }
cds. parse. xpr()
Convenience shortcut to cds.parse.expr(x).xpr
Example:
let xpr = cds.parse.xpr (`foo.bar > 9`)
//> [ {ref:['foo', 'bar']}, '>', {val:9} ]
let xpr = cds.parse.xpr (`foo.bar > 9`)
//> [ {ref:['foo', 'bar']}, '>', {val:9} ]
cds. parse. ref()
Convenience shortcut to cds.parse.expr(x).ref
Example:
let ref = cds.parse.ref (`foo.bar`)
//>= ['foo', 'bar']
let ref = cds.parse.ref (`foo.bar`)
//>= ['foo', 'bar']
cds. minify()
Minifies a given CSN model by removing all unused1 types and aspects, as well all entities tagged with @cds.persistence.skip:'if-unused'
. Use it like that:
let csn = await cds.load('*').then(cds.minify)
let csn = await cds.load('*').then(cds.minify)
Using cds.minify()
is particularly relevant, when reuse models are in the game. For example, this applies to @sap/cds/common
. In there, all code list entities like Countries, Currencies and Languages are tagged with @cds.persistence.skip:'if-unused'
. For example, run this in cap/samples/bookshop:
[bookshop] DEBUG=minify cds -e "cds.load('*').then(cds.minify)"
[bookshop] DEBUG=minify cds -e "cds.load('*').then(cds.minify)"
... would generate this output, informing which definitions got skipped:
[minify] - skipping type Language
[minify] - skipping type Country
[minify] - skipping context sap.common
[minify] - skipping entity sap.common.Languages
[minify] - skipping entity sap.common.Countries
[minify] - skipping aspect cuid
[minify] - skipping aspect temporal
[minify] - skipping aspect extensible
[minify] - skipping entity sap.common.Languages.texts
[minify] - skipping entity sap.common.Countries.texts
[minify] - skipping type Language
[minify] - skipping type Country
[minify] - skipping context sap.common
[minify] - skipping entity sap.common.Languages
[minify] - skipping entity sap.common.Countries
[minify] - skipping aspect cuid
[minify] - skipping aspect temporal
[minify] - skipping aspect extensible
[minify] - skipping entity sap.common.Languages.texts
[minify] - skipping entity sap.common.Countries.texts
1 Unused in that context means, not reachable from roots services and — non-skipped — entities in the model.
cds. resolve()
Resolves the given source paths by fetching matching model source files, that is .cds or .csn files, including models for required services. In detail, it works as follows:
- If
paths
is'*'
:paths
= [ ...cds.env.roots
, ...cds.requires.<srv>.model
] - If
paths
is a single string:paths
= [paths
] - For
<each>
inpaths
: ...
- if <each>.csn|cds exists → use it
- if <each>/index.csn|cds exists → use it
- if <each> is a folder → use all .csn|cds found in there
In effect, it resolves and returns an array with the absolute filenames of the root cds model files to be used to invoke the compiler.
If no files are found, undefined
is returned.
Examples:
cds.env.folders // = folders db, srv, app by default
cds.env.roots // + schema and services in cwd
cds.resolve('*',false) // + models in cds.env.requires
cds.resolve('*') // > the resolved existing files
cds.resolve(['db']) // > the resolved existing files
cds.resolve(['db','srv']) // > the resolved existing files
cds.resolve('none') // > undefined
cds.env.folders // = folders db, srv, app by default
cds.env.roots // + schema and services in cwd
cds.resolve('*',false) // + models in cds.env.requires
cds.resolve('*') // > the resolved existing files
cds.resolve(['db']) // > the resolved existing files
cds.resolve(['db','srv']) // > the resolved existing files
cds.resolve('none') // > undefined
Try this in cds repl launched from your project root to see that in action.