UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

79 lines (69 loc) 3.15 kB
const cds = require('..'), { path, isfile } = cds.utils /** * NOTE: Need this typed helper variable to be able to use IntelliSense for calls with new keyword. * @import Service from './cds.Service' * @type new() => Service & Promise<Service> */ const factory = ServiceFactory module.exports = exports = factory function ServiceFactory (name, model, options) { const o = { ...options } // avoid changing shared options const def = model?.definitions[name] || {} const remote = o.external && (o.credentials || !o.mocked) return _use (remote ? o.impl : o.with || def['@impl'] || _sibling(def) || o.impl || _kind()) async function _use (impl) { switch (typeof impl) { case 'function': if (impl._is_service_class) return new impl (name, model, o) return _use (_kind(), /*with:*/ o.impl = _legacy(impl) || impl) case 'object': return _use (impl[name] || impl.default || _kind()) case 'string': if (impl.startsWith('@sap/cds/')) impl = cds.home + impl.slice(8) //> for local tests in @sap/cds dev if (impl.startsWith('./')) impl = path.join (_source4(def) || 'x', '.'+impl) try { var resolved = require.resolve (path.join (cds.root, impl)) } catch (e) { // fetch local paths try { resolved = require.resolve (impl, {paths:[ cds.root, cds.home ]}) } catch { // fetch in node_modules throw cds.error(`Failed loading service implementation from ` + impl, { cause: e }) } } impl = await cds.utils._import (resolved) impl = await _use (impl) impl._source = resolved return impl default: throw cds.error`Invalid service implementation for ${name}: ${impl}` }} function _kind (kind = o.kind ??= def['@kind'] || 'app-service') { const {impl} = cds.requires[kind] || cds.requires.kinds[kind] || cds.error `No configuration found for 'cds.requires.${kind}'` return impl || cds.error `No 'impl' configured for 'cds.requires.kinds.${kind}'` } } const exts = process.env.CDS_TYPESCRIPT ? ['.ts','.js','.mjs'] : ['.js','.mjs'] const _source4 = d => d['@source'] || d.$location?.file const _sibling = d => { let file = _source4(d); if (!file) return let { dir, name } = path.parse (file); if (!dir) dir = '.' for (let subdir of ['/', '/lib/', '/handlers/']) { for (let ext of exts) try { const impl = dir + subdir + name + ext return isfile(impl) || require.resolve (impl, {paths:[ cds.root, cds.home ]}) } catch(e) { if (e.code !== 'MODULE_NOT_FOUND') throw e } } } const _legacy = impl => { // legacy hello-world style class if (impl.prototype && /^class\b/.test(impl)) return function() { const legacy = new impl for (let k of Reflect.ownKeys(impl.prototype)) k === 'constructor' || k === 'prototype' || this.on(k, legacy[k].bind(legacy)) } } /** * @protected Called by cds.connect() and cds.serve() for cds.service.impl-style implementations. * @param {Service} srv */ exports.init = async function (srv) { const {impl} = srv.options; if (typeof impl === 'function' && !impl._is_service_class) await impl.call(srv, srv) await srv.init() return srv }