1+ import { AsyncLocalStorage } from 'async_hooks' ;
12import crypto from 'crypto' ;
23import vm from 'vm' ;
34import fs from 'fs' ;
@@ -23,6 +24,8 @@ import { YamlCompiler } from './YamlCompiler';
2324import { CubeDictionary } from './CubeDictionary' ;
2425import { CompilerCache } from './CompilerCache' ;
2526
27+ const ctxFileStorage = new AsyncLocalStorage < FileContent > ( ) ;
28+
2629const NATIVE_IS_SUPPORTED = isNativeSupported ( ) ;
2730
2831const moduleFileCache = { } ;
@@ -231,7 +234,7 @@ export class DataSchemaCompiler {
231234 ) ;
232235 }
233236
234- const transpile = async ( stage : CompileStage ) => {
237+ const transpile = async ( stage : CompileStage ) : Promise < FileContent [ ] > => {
235238 let cubeNames : string [ ] = [ ] ;
236239 let cubeSymbols : Record < string , Record < string , boolean > > = { } ;
237240 let transpilerNames : string [ ] = [ ] ;
@@ -306,34 +309,62 @@ export class DataSchemaCompiler {
306309 let contexts : Record < string , any > [ ] = [ ] ;
307310 let compiledFiles : Record < string , boolean > = { } ;
308311 let asyncModules : CallableFunction [ ] = [ ] ;
312+ let transpiledFiles : FileContent [ ] = [ ] ;
309313
310314 this . compileV8ContextCache = vm . createContext ( {
311- view : ( name , cube ) => (
312- ! cube ?
315+ view : ( name , cube ) => {
316+ const file = ctxFileStorage . getStore ( ) ;
317+ if ( ! file ) {
318+ throw new Error ( 'No file stored in context' ) ;
319+ }
320+ return ! cube ?
313321 this . cubeFactory ( { ...name , fileName : file . fileName , isView : true } ) :
314- cubes . push ( { ...cube , name, fileName : file . fileName , isView : true } )
315- ) ,
316- cube : ( name , cube ) => (
317- ! cube ?
322+ cubes . push ( { ...cube , name, fileName : file . fileName , isView : true } ) ;
323+ } ,
324+ cube : ( name , cube ) => {
325+ const file = ctxFileStorage . getStore ( ) ;
326+ if ( ! file ) {
327+ throw new Error ( 'No file stored in context' ) ;
328+ }
329+ return ! cube ?
318330 this . cubeFactory ( { ...name , fileName : file . fileName } ) :
319- cubes . push ( { ...cube , name, fileName : file . fileName } )
320- ) ,
321- context : ( name , context ) => contexts . push ( { ...context , name, fileName : file . fileName } ) ,
331+ cubes . push ( { ...cube , name, fileName : file . fileName } ) ;
332+ } ,
333+ context : ( name : string , context ) => {
334+ const file = ctxFileStorage . getStore ( ) ;
335+ if ( ! file ) {
336+ throw new Error ( 'No file stored in context' ) ;
337+ }
338+ return contexts . push ( { ...context , name, fileName : file . fileName } ) ;
339+ } ,
322340 addExport : ( obj ) => {
341+ const file = ctxFileStorage . getStore ( ) ;
342+ if ( ! file ) {
343+ throw new Error ( 'No file stored in context' ) ;
344+ }
323345 exports [ file . fileName ] = exports [ file . fileName ] || { } ;
324346 exports [ file . fileName ] = Object . assign ( exports [ file . fileName ] , obj ) ;
325347 } ,
326348 setExport : ( obj ) => {
349+ const file = ctxFileStorage . getStore ( ) ;
350+ if ( ! file ) {
351+ throw new Error ( 'No file stored in context' ) ;
352+ }
327353 exports [ file . fileName ] = obj ;
328354 } ,
329355 asyncModule : ( fn ) => {
330356 asyncModules . push ( fn ) ;
331357 } ,
332- require : ( extensionName ) => {
358+ require : ( extensionName : string ) => {
359+ const file = ctxFileStorage . getStore ( ) ;
360+ if ( ! file ) {
361+ throw new Error ( 'No file stored in context' ) ;
362+ }
363+
333364 if ( this . extensions [ extensionName ] ) {
334365 return new ( this . extensions [ extensionName ] ) ( this . cubeFactory , this , cubes ) ;
335366 } else {
336- const foundFile = this . resolveModuleFile ( file , extensionName , toCompile , errorsReport ) ;
367+ const foundFile = this . resolveModuleFile ( file , extensionName , transpiledFiles , errorsReport ) ;
337368 if ( ! foundFile && this . allowNodeRequire ) {
338369 if ( extensionName . indexOf ( '.' ) === 0 ) {
339370 extensionName = path . resolve ( this . repository . localPath ( ) , extensionName ) ;
@@ -360,14 +391,16 @@ export class DataSchemaCompiler {
360391 } ) ;
361392
362393 const compilePhase = async ( compilers : CompileCubeFilesCompilers , stage : 0 | 1 | 2 | 3 ) => {
363- const res = this . compileCubeFiles ( cubes , contexts , compiledFiles , asyncModules , compilers , await transpile ( stage ) , errorsReport ) ;
394+ transpiledFiles = await transpile ( stage ) ;
395+ const res = this . compileCubeFiles ( cubes , contexts , compiledFiles , asyncModules , compilers , transpiledFiles , errorsReport ) ;
364396
365397 // clear the objects for the next phase
366398 cubes = [ ] ;
367399 exports = { } ;
368400 contexts = [ ] ;
369401 compiledFiles = { } ;
370402 asyncModules = [ ] ;
403+ transpiledFiles = [ ] ;
371404
372405 return res ;
373406 } ;
@@ -676,7 +709,13 @@ export class DataSchemaCompiler {
676709
677710 try {
678711 const script = this . getJsScript ( file ) ;
679- script . runInContext ( this . compileV8ContextCache ! , { timeout : 15000 } ) ;
712+
713+ // We use AsyncLocalStorage to store the current file context
714+ // so that it can be accessed in the script execution context even within async functions.
715+ // @see https://nodejs.org/api/async_context.html
716+ ctxFileStorage . run ( file , ( ) => {
717+ script . runInContext ( this . compileV8ContextCache ! , { timeout : 15000 } ) ;
718+ } ) ;
680719 } catch ( e ) {
681720 errorsReport . error ( e ) ;
682721 }
0 commit comments