Parsing and Compiling Models
Use cds.compile
, or one of the convenience shortcuts cds.parse
and cds.load
to parse and compile CDS models programmatically.
cds.compile (models, options)
This is the central function and API facade to parse and compile models.
It supports different variants based on the type of the models
argument as outlined in the following subsections.
async cds.compile (‘file:…’, options) ⇢ csn
async cds.compile ([files], options) ⇢ csn
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.
Examples:
let csn = await cds.compile ('file:db')
let csn = await cds.compile (['db','srv','app'])
The given filenames are resolved to effective absolute filenames using cds.resolve
.
cds.compile (‘cdl’, options) → csn
If a single CDL string is passed as first argument, that CDL source is compiled to CSN synchroneously.
Note: using from
clauses are not resolved in this usage.
Example:
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 }
`)
cds.compile ({sources}, options) → csn
Allows to synchronously compile multiple named CDL or CSN sources, which allows to also resolve using from
clauses.
Example:
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" }
}}
}}
`,
})
Options
All variants of cds.compile()
support the following options
:
options.flavor
One of:
'parsed'
→ generates a parsed-only CSN'inferred'
→ + all query signatures inferred
options.min
If true
, definitions not reachable from service roots will be deleted.
Use cds.env.features.skip_unused
to control how the respective tree shaking mechanism works:
false
→ switched offtrue
→ remove only definitions annotated with@cds.persistence.skip:'if-unused'
'all'
→ remove all definitions not reachable from services
options.docs
If true
, doc comments (/**...*/
) are preserves in generated CSN
options.locations
If true
, all $location
properties are preserved in generated CSN
options.messages
Pass an empty array to collect all compiler messages in it.
cds.compile.for/to …
These are collection of model processors take a CDS model as input and generate different outputs.
Static and Fluent API Variants
All methods are available in a static and fluent variants.
Static variants are invoked like that::
let odata = cds.compile.for.odata(csn)
let yaml = cds.compile.to.yml(csn)
Fluent API variants are invoked like that:
let odata = cds.compile(model).for.odata()
let yaml = cds.compile(model).to.yaml()
While static variants expect a parsed CSN, fluent variants also accept all of the source inputs as documented for cds.compile
above.
↳ .for.odata (csn, options) → unfolded csn
Returns a new csn with unfolded structs and associations, depending on the specified options
.
In addition resolves certain common annotations to their OData vocabulary counterparts.
Accepted options
are those documented in the OData guide without cds.odata.
prefix.
If the options
argument is a single string, it is interpreted as a shortcut to {flavor:...}
Examples:
let o1 = cds.compile(csn).for.odata ({
containment:true,
version:'v4',
})
let o2 = cds.compile(csn).for.odata ({flavor:'x4'})
let o3 = cds.compile(csn).for.odata ('x4') //> shortcut to above
Idempotent behavoir — Calling this function again on the same csn input will return the cached unfolded csn output from the former call.
↳ .to.json/yaml (csn) → json
Renders the given model to a formatted JSON or YAML string.
↳ .to.edm/edmx (csn, options) → edm
Compiles and returns an OData v4 EDM, repectively 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(csn).to.edm ({service:'Catalog'})
console.log (edm)
// for all services
let all = cds.compile(csn).to.edm ({service:'all'})
for (let [edm,{name}] of all) console.log (name,edm)
↳ .to.hdbtable (csn) → …
↳ .to.hdbcds (csn) → …
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)
↳ .to.sql (csn, options) → SQL DDL
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'})
cds.parse (cdl) → csn
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. The individual methods are:
CDL
, cds.parse.cdl (cdl) → csn
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{}`)
CQL
, cds.parse.cql (cql) → cqn
Parses a source string in CQL syntax and returns it as a parsed query according to the CQN spec.
Examples:
let cqn = CQL`SELECT * from Foo`
let cqn = cds.parse.cql (`SELECT * from Foo`)
CXL
, cds.parse.expr (cxl) → cxn
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} ] }
cds.parse.xpr (cxl) → xpr
Convenience shortcut to cds.parse.expr(x).xpr
Example:
let xpr = cds.parse.xpr (`foo.bar > 9`)
//> [ {ref:['foo', 'bar']}, '>', {val:9} ]
cds.parse.ref (cxl) → ref
Convenience shortcut to cds.parse.expr(x).ref
Example:
let ref = cds.parse.ref (`foo.bar`)
//>= ['foo', 'bar']
cds.load (files) ⇢ csn
Loads and parses a model from one or more files into a single effective model.
It’s essentially a shortcut to cds.compile (..., {flavor:'inferred'})
.
Arguments:
files
— a single filename, or an array of such, resolved usingcds.resolve()
options
— options passed tocds.compile()
]
Note: It’s recommended to omit file suffixes to leverage automatic loading from precompiled CSN files instead of CDL sources.
Usages:
// 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 relative to current working directory
cds.load('relative-to-cwd')
cds.load('./relative-to-cwd')
// load relative to current node module
cds.load(__dirname+'/relative-to-this-module')
cds.resolve (paths) → [filenames]
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
Try this in cds repl launched from your project root to see that in action.