@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
94 lines (82 loc) • 4.15 kB
JavaScript
const path = require('path')
const cds = require('../../cds')
const { DEFAULT_CSN_FILE_NAME, OUTPUT_MODE, OUTPUT_MODE_RESULT } = require('../constants')
const InternalBuildPlugin = require('./internalBuildPlugin')
const { getI18nDefaultFolder, filterFeaturePaths } = require('../util')
class FeatureToggleBuildPlugin extends InternalBuildPlugin {
init() {
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
this._result = {
dest: this.task.dest,
edmx: new Map(),
languages: new Set(),
services: new Set()
}
}
}
async compileAll(csn, destBase, destFts) {
const sources = filterFeaturePaths(csn, this.task.options.model || this.task.src)
const dictionary = { base: null, features: null }
if (sources.features) {
// create base model as the given CSN is containing all features
dictionary.base = await cds.load(sources.base, this.options())
} else {
// CSN already represents the base model as no features exist
dictionary.base = csn
}
// preserve @location as @source properties
const csnStr = await this.compileToJson(dictionary.base, path.join(destBase, DEFAULT_CSN_FILE_NAME))
const csnModel = JSON.parse(csnStr)
csnModel.meta = csn.meta
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
this._result.csn = csnModel
}
if (sources.features) {
dictionary.features = await this._compileFeatures(sources.features, destBase, destFts, dictionary.base)
}
return { dictionary, sources }
}
async collectAllLanguageBundles(dictionary, paths, destBase, destFts) {
const i18nFolder = getI18nDefaultFolder()
const ftsName = path.dirname(cds.env.features.folders || 'fts/*')
// create language bundle for base model
const i18n = await this.collectLanguageBundles(dictionary.base, path.join(destBase, i18nFolder))
if (i18n && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
this._result.languageBundles = i18n.bundles
}
if (dictionary.features) {
// create language bundles for all features
for (const ftName in dictionary.features) {
// attach the sources information for i18n location reference
dictionary.features[ftName]['$sources'] = paths.features[ftName]
await this.collectLanguageBundles(dictionary.features[ftName], path.join(destFts, ftsName, ftName, i18nFolder))
}
}
}
async _compileFeatures(ftsPaths, destBase, destFts, baseCsn) {
if (!ftsPaths) {
return
}
const features = {}
const options = { ...this.options(), flavor: 'parsed' }
const ftsName = path.dirname(cds.env.features.folders || 'fts/*')
// create feature models
for (const ftName in ftsPaths) {
const ftCsn = await cds.load(ftsPaths[ftName], options)
const ftPath = path.join(destFts, ftsName, ftName)
// replace require paths by base model path to ensure precedence of feature annotations
// see https://pages.github.tools.sap/cap/docs/cds/compiler-messages#anno-duplicate-unrelated-layer
ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g, '/')]
await this.compileToJson(ftCsn, path.join(ftPath, DEFAULT_CSN_FILE_NAME))
this._validateFeature(ftCsn, baseCsn)
features[ftName] = ftCsn
}
return features
}
_validateFeature(ftCsn, baseCsn) {
// features must not have other dependencies than base model dependencies - cross feature dependencies are not supported
// assuming only core compiler options are relevant for validation scenario
cds.compiler.compileSources({ [ftCsn.requires[0]]: baseCsn, 'feature.csn': ftCsn }, { ...super.options(), ...cds.env.cdsc ?? {} })
}
}
module.exports = FeatureToggleBuildPlugin