@@ -19,7 +19,7 @@ import strings from '../../../resources';
1919import * as urlBuilder from '../../domain/url-builder' ;
2020import * as validator from '../../domain/validators' ;
2121import type { Repository } from '../../domain/repository' ;
22- import { Config } from '../../../types' ;
22+ import { Component , Config } from '../../../types' ;
2323import { IncomingHttpHeaders } from 'http' ;
2424import { fromPromise } from 'universalify' ;
2525
@@ -54,13 +54,33 @@ export interface GetComponentResult {
5454 } ;
5555}
5656
57+ const noopConsole = Object . fromEntries (
58+ Object . keys ( console ) . map ( key => [ key , _ . noop ] )
59+ ) ;
60+
5761export default function getComponent ( conf : Config , repository : Repository ) {
5862 const client = Client ( { templates : conf . templates } ) ;
5963 const cache = new Cache ( {
6064 verbose : ! ! conf . verbosity ,
6165 refreshInterval : conf . refreshInterval
6266 } ) ;
6367
68+ const getEnv = async (
69+ component : Component
70+ ) : Promise < Record < string , string > > => {
71+ const cacheKey = `${ component . name } /${ component . version } /.env` ;
72+ const cached = cache . get ( 'file-contents' , cacheKey ) ;
73+
74+ if ( cached ) return cached ;
75+
76+ const env = component . oc . files . env
77+ ? await repository . getEnv ( component . name , component . version )
78+ : { } ;
79+ cache . set ( 'file-contents' , cacheKey , env ) ;
80+
81+ return env ;
82+ } ;
83+
6484 const renderer = function (
6585 options : RendererOptions ,
6686 cb : ( result : GetComponentResult ) => void
@@ -411,136 +431,149 @@ export default function getComponent(conf: Config, repository: Repository) {
411431 if ( ! component . oc . files . dataProvider ) {
412432 returnComponent ( null , { } ) ;
413433 } else {
414- const cacheKey = `${ component . name } /${ component . version } /server.js` ;
415- const cached = cache . get ( 'file-contents' , cacheKey ) ;
416- const domain = Domain . create ( ) ;
417- const setEmptyResponse =
418- emptyResponseHandler . contextDecorator ( returnComponent ) ;
419- const contextObj = {
420- acceptLanguage : acceptLanguageParser . parse ( acceptLanguage ! ) ,
421- baseUrl : conf . baseUrl ,
422- env : conf . env ,
423- params,
424- plugins : conf . plugins ,
425- renderComponent : fromPromise ( nestedRenderer . renderComponent ) ,
426- renderComponents : fromPromise ( nestedRenderer . renderComponents ) ,
427- requestHeaders : options . headers ,
428- requestIp : options . ip ,
429- setEmptyResponse,
430- staticPath : repository
431- . getStaticFilePath ( component . name , component . version , '' )
432- . replace ( 'https:' , '' ) ,
433- setHeader : ( header ?: string , value ?: string ) => {
434- if ( ! ( typeof header === 'string' && typeof value === 'string' ) ) {
435- throw strings . errors . registry
436- . COMPONENT_SET_HEADER_PARAMETERS_NOT_VALID ;
437- }
438-
439- if ( header && value ) {
440- responseHeaders = responseHeaders || { } ;
441- responseHeaders [ header . toLowerCase ( ) ] = value ;
442- }
443- } ,
444- templates : repository . getTemplatesInfo ( )
445- } ;
446-
447- const setCallbackTimeout = ( ) => {
448- const executionTimeout = conf . executionTimeout ;
449- if ( executionTimeout ) {
450- setTimeout ( ( ) => {
451- const message = `timeout (${ executionTimeout * 1000 } ms)` ;
452- returnComponent ( { message } , undefined ) ;
453- domain . exit ( ) ;
454- } , executionTimeout * 1000 ) ;
434+ fromPromise ( getEnv ) ( component , ( err , env ) => {
435+ if ( err ) {
436+ componentCallbackDone = true ;
437+
438+ return callback ( {
439+ status : 502 ,
440+ response : {
441+ code : 'ENV_RESOLVING_ERROR' ,
442+ error : strings . errors . registry . RESOLVING_ERROR
443+ }
444+ } ) ;
455445 }
456- } ;
457446
458- if ( ! ! cached && ! conf . hotReloading ) {
459- domain . on ( 'error' , returnComponent ) ;
447+ const cacheKey = `${ component . name } /${ component . version } /server.js` ;
448+ const cached = cache . get ( 'file-contents' , cacheKey ) ;
449+ const domain = Domain . create ( ) ;
450+ const setEmptyResponse =
451+ emptyResponseHandler . contextDecorator ( returnComponent ) ;
452+ const contextObj = {
453+ acceptLanguage : acceptLanguageParser . parse ( acceptLanguage ! ) ,
454+ baseUrl : conf . baseUrl ,
455+ env : { ...conf . env , ...env } ,
456+ params,
457+ plugins : conf . plugins ,
458+ renderComponent : fromPromise ( nestedRenderer . renderComponent ) ,
459+ renderComponents : fromPromise ( nestedRenderer . renderComponents ) ,
460+ requestHeaders : options . headers ,
461+ requestIp : options . ip ,
462+ setEmptyResponse,
463+ staticPath : repository
464+ . getStaticFilePath ( component . name , component . version , '' )
465+ . replace ( 'https:' , '' ) ,
466+ setHeader : ( header ?: string , value ?: string ) => {
467+ if (
468+ ! ( typeof header === 'string' && typeof value === 'string' )
469+ ) {
470+ throw strings . errors . registry
471+ . COMPONENT_SET_HEADER_PARAMETERS_NOT_VALID ;
472+ }
460473
461- try {
462- domain . run ( ( ) => {
463- cached ( contextObj , returnComponent ) ;
464- setCallbackTimeout ( ) ;
465- } ) ;
466- } catch ( e ) {
467- return returnComponent ( e , undefined ) ;
468- }
469- } else {
470- fromPromise ( repository . getDataProvider ) (
471- component . name ,
472- component . version ,
473- ( err , dataProvider ) => {
474- if ( err ) {
475- componentCallbackDone = true ;
476-
477- return callback ( {
478- status : 502 ,
479- response : {
480- code : 'DATA_RESOLVING_ERROR' ,
481- error : strings . errors . registry . RESOLVING_ERROR
482- }
483- } ) ;
474+ if ( header && value ) {
475+ responseHeaders = responseHeaders || { } ;
476+ responseHeaders [ header . toLowerCase ( ) ] = value ;
484477 }
478+ } ,
479+ templates : repository . getTemplatesInfo ( )
480+ } ;
485481
486- const context = {
487- require : RequireWrapper ( conf . dependencies ) ,
488- module : {
489- exports : { } as Record <
490- string ,
491- ( ...args : unknown [ ] ) => unknown
492- >
493- } ,
494- console : conf . local ? console : Object . fromEntries ( Object . keys ( console ) . map ( key => [ key , _ . noop ] ) ) ,
495- setTimeout,
496- Buffer
497- } ;
498-
499- const handleError = ( err : {
500- code : string ;
501- missing : string [ ] ;
502- } ) => {
503- if ( err . code === 'DEPENDENCY_MISSING_FROM_REGISTRY' ) {
482+ const setCallbackTimeout = ( ) => {
483+ const executionTimeout = conf . executionTimeout ;
484+ if ( executionTimeout ) {
485+ setTimeout ( ( ) => {
486+ const message = `timeout (${ executionTimeout * 1000 } ms)` ;
487+ returnComponent ( { message } , undefined ) ;
488+ domain . exit ( ) ;
489+ } , executionTimeout * 1000 ) ;
490+ }
491+ } ;
492+
493+ if ( ! ! cached && ! conf . hotReloading ) {
494+ domain . on ( 'error' , returnComponent ) ;
495+
496+ try {
497+ domain . run ( ( ) => {
498+ cached ( contextObj , returnComponent ) ;
499+ setCallbackTimeout ( ) ;
500+ } ) ;
501+ } catch ( e ) {
502+ return returnComponent ( e , undefined ) ;
503+ }
504+ } else {
505+ fromPromise ( repository . getDataProvider ) (
506+ component . name ,
507+ component . version ,
508+ ( err , dataProvider ) => {
509+ if ( err ) {
504510 componentCallbackDone = true ;
505511
506512 return callback ( {
507- status : 501 ,
513+ status : 502 ,
508514 response : {
509- code : err . code ,
510- error : strings . errors . registry . DEPENDENCY_NOT_FOUND (
511- err . missing . join ( ', ' )
512- ) ,
513- missingDependencies : err . missing
515+ code : 'DATA_RESOLVING_ERROR' ,
516+ error : strings . errors . registry . RESOLVING_ERROR
514517 }
515518 } ) ;
516519 }
517520
518- returnComponent ( err , undefined ) ;
519- } ;
520-
521- const options = conf . local
522- ? {
523- displayErrors : true ,
524- filename : dataProvider . filePath
521+ const context = {
522+ require : RequireWrapper ( conf . dependencies ) ,
523+ module : {
524+ exports : { } as Record < string , ( ...args : any [ ] ) => any >
525+ } ,
526+ console : conf . local ? console : noopConsole ,
527+ setTimeout,
528+ Buffer
529+ } ;
530+
531+ const handleError = ( err : {
532+ code : string ;
533+ missing : string [ ] ;
534+ } ) => {
535+ if ( err . code === 'DEPENDENCY_MISSING_FROM_REGISTRY' ) {
536+ componentCallbackDone = true ;
537+
538+ return callback ( {
539+ status : 501 ,
540+ response : {
541+ code : err . code ,
542+ error : strings . errors . registry . DEPENDENCY_NOT_FOUND (
543+ err . missing . join ( ', ' )
544+ ) ,
545+ missingDependencies : err . missing
546+ }
547+ } ) ;
525548 }
526- : { } ;
527549
528- try {
529- vm . runInNewContext ( dataProvider . content , context , options ) ;
530- const processData = context . module . exports [ 'data' ] ;
531- cache . set ( 'file-contents' , cacheKey , processData ) ;
550+ returnComponent ( err , undefined ) ;
551+ } ;
532552
533- domain . on ( 'error' , handleError ) ;
534- domain . run ( ( ) => {
535- processData ( contextObj , returnComponent ) ;
536- setCallbackTimeout ( ) ;
537- } ) ;
538- } catch ( err ) {
539- handleError ( err as any ) ;
553+ const options = conf . local
554+ ? {
555+ displayErrors : true ,
556+ filename : dataProvider . filePath
557+ }
558+ : { } ;
559+
560+ try {
561+ vm . runInNewContext ( dataProvider . content , context , options ) ;
562+ const processData = context . module . exports [ 'data' ] ;
563+ cache . set ( 'file-contents' , cacheKey , processData ) ;
564+
565+ domain . on ( 'error' , handleError ) ;
566+ domain . run ( ( ) => {
567+ processData ( contextObj , returnComponent ) ;
568+ setCallbackTimeout ( ) ;
569+ } ) ;
570+ } catch ( err ) {
571+ handleError ( err as any ) ;
572+ }
540573 }
541- }
542- ) ;
543- }
574+ ) ;
575+ }
576+ } ) ;
544577 }
545578 }
546579 ) ;
0 commit comments